Prechádzať zdrojové kódy

Merge branch 'master' into coraza_poc_acquis

Thibault "bui" Koechlin 2 rokov pred
rodič
commit
ee8b31348b
100 zmenil súbory, kde vykonal 1058 pridanie a 787 odobranie
  1. 1 0
      .dockerignore
  2. 10 0
      .github/codecov.yml
  3. 1 1
      .github/workflows/bats-hub.yml
  4. 1 1
      .github/workflows/bats-mysql.yml
  5. 1 19
      .github/workflows/bats-postgres.yml
  6. 1 2
      .github/workflows/bats-sqlite-coverage.yml
  7. 1 1
      .github/workflows/ci-windows-build-msi.yml
  8. 1 1
      .github/workflows/go-tests-windows.yml
  9. 1 1
      .github/workflows/go-tests.yml
  10. 70 0
      .github/workflows/publish_docker-image_on_master-debian.yml
  11. 8 4
      .github/workflows/release_publish-package.yml
  12. 2 2
      .github/workflows/release_publish_docker-image-debian.yml
  13. 7 0
      .gitignore
  14. 12 2
      Dockerfile
  15. 5 2
      Dockerfile.debian
  16. 76 71
      Makefile
  17. 1 1
      azure-pipelines.yml
  18. 0 2
      cmd/crowdsec-cli/Makefile
  19. 3 2
      cmd/crowdsec-cli/alerts.go
  20. 3 5
      cmd/crowdsec-cli/bouncers.go
  21. 4 3
      cmd/crowdsec-cli/capi.go
  22. 1 0
      cmd/crowdsec-cli/config.go
  23. 24 0
      cmd/crowdsec-cli/config_showyaml.go
  24. 9 7
      cmd/crowdsec-cli/console.go
  25. 64 28
      cmd/crowdsec-cli/decisions.go
  26. 10 1
      cmd/crowdsec-cli/hub.go
  27. 20 18
      cmd/crowdsec-cli/lapi.go
  28. 4 3
      cmd/crowdsec-cli/machines.go
  29. 5 2
      cmd/crowdsec-cli/main.go
  30. 43 28
      cmd/crowdsec-cli/metrics.go
  31. 3 2
      cmd/crowdsec-cli/notifications.go
  32. 8 6
      cmd/crowdsec-cli/papi.go
  33. 3 2
      cmd/crowdsec-cli/simulation.go
  34. 3 1
      cmd/crowdsec-cli/support.go
  35. 12 36
      cmd/crowdsec-cli/utils.go
  36. 2 4
      cmd/crowdsec/Makefile
  37. 4 3
      cmd/crowdsec/api.go
  38. 7 5
      cmd/crowdsec/crowdsec.go
  39. 4 2
      cmd/crowdsec/main.go
  40. 5 4
      cmd/crowdsec/metrics.go
  41. 3 2
      cmd/crowdsec/output.go
  42. 5 4
      cmd/crowdsec/run_in_svc.go
  43. 5 4
      cmd/crowdsec/run_in_svc_windows.go
  44. 3 1
      cmd/crowdsec/serve.go
  45. 24 1
      config/acquis_win.yaml
  46. 1 2
      config/simulation.yaml
  47. 0 6
      debian/rules
  48. 1 0
      docker/README.md
  49. 5 3
      docker/docker_start.sh
  50. 2 2
      docker/test/Pipfile
  51. 77 112
      docker/test/Pipfile.lock
  52. 32 0
      docker/test/tests/test_capi_whitelists.py
  53. 7 5
      go.mod
  54. 12 7
      go.sum
  55. 12 4
      mk/goversion.mk
  56. 5 0
      mk/platform.mk
  57. 0 2
      mk/platform/freebsd.mk
  58. 0 2
      mk/platform/linux.mk
  59. 0 2
      mk/platform/openbsd.mk
  60. 0 3
      mk/platform/unix_common.mk
  61. 0 7
      mk/platform/windows.mk
  62. 4 2
      pkg/acquisition/acquisition.go
  63. 2 1
      pkg/acquisition/acquisition_test.go
  64. 2 1
      pkg/acquisition/modules/cloudwatch/cloudwatch_test.go
  65. 2 1
      pkg/acquisition/modules/docker/docker_test.go
  66. 7 4
      pkg/acquisition/modules/file/file.go
  67. 2 1
      pkg/acquisition/modules/file/file_test.go
  68. 4 2
      pkg/acquisition/modules/journalctl/journalctl.go
  69. 2 1
      pkg/acquisition/modules/journalctl/journalctl_test.go
  70. 10 2
      pkg/acquisition/modules/kafka/kafka.go
  71. 2 1
      pkg/acquisition/modules/kafka/kafka_test.go
  72. 4 2
      pkg/acquisition/modules/kinesis/kinesis.go
  73. 2 1
      pkg/acquisition/modules/kinesis/kinesis_test.go
  74. 3 1
      pkg/acquisition/modules/kubernetesaudit/k8s_audit.go
  75. 2 1
      pkg/acquisition/modules/syslog/internal/parser/rfc5424/parse_test.go
  76. 3 1
      pkg/acquisition/modules/syslog/syslog.go
  77. 2 1
      pkg/acquisition/modules/syslog/syslog_test.go
  78. 3 1
      pkg/acquisition/modules/wineventlog/wineventlog_windows.go
  79. 4 2
      pkg/alertcontext/alertcontext.go
  80. 6 5
      pkg/apiclient/alerts_service_test.go
  81. 6 5
      pkg/apiclient/auth_service_test.go
  82. 4 3
      pkg/apiclient/client_http_test.go
  83. 7 6
      pkg/apiclient/client_test.go
  84. 7 5
      pkg/apiclient/decisions_service.go
  85. 6 5
      pkg/apiclient/decisions_service_test.go
  86. 3 2
      pkg/apiclient/heartbeat.go
  87. 5 5
      pkg/apiserver/alerts_test.go
  88. 1 1
      pkg/apiserver/api_key_test.go
  89. 35 31
      pkg/apiserver/apic.go
  90. 144 142
      pkg/apiserver/apic_test.go
  91. 11 3
      pkg/apiserver/apiserver.go
  92. 26 14
      pkg/apiserver/apiserver_test.go
  93. 1 1
      pkg/apiserver/jwt_test.go
  94. 5 5
      pkg/apiserver/machines_test.go
  95. 4 3
      pkg/apiserver/papi.go
  96. 16 14
      pkg/apiserver/papi_cmd.go
  97. 53 45
      pkg/csconfig/api.go
  98. 10 10
      pkg/csconfig/api_test.go
  99. 10 11
      pkg/csconfig/config.go
  100. 4 4
      pkg/csconfig/config_test.go

+ 1 - 0
.dockerignore

@@ -3,3 +3,4 @@
 #.git
 
 /tests
+/crowdsec-v*

+ 10 - 0
.github/codecov.yml

@@ -0,0 +1,10 @@
+# we measure coverage but don't enforce it
+# https://docs.codecov.com/docs/codecov-yaml
+coverage:
+  status:
+    patch:
+      default:
+        target: 0%
+    project:
+      default:
+        target: 0%

+ 1 - 1
.github/workflows/bats-hub.yml

@@ -15,7 +15,7 @@ jobs:
   build:
     strategy:
       matrix:
-        go-version: ["1.20.3"]
+        go-version: ["1.20.4"]
 
     name: "Build + tests"
     runs-on: ubuntu-latest

+ 1 - 1
.github/workflows/bats-mysql.yml

@@ -14,7 +14,7 @@ jobs:
   build:
     strategy:
       matrix:
-        go-version: ["1.20.3"]
+        go-version: ["1.20.4"]
 
     name: "Build + tests"
     runs-on: ubuntu-latest

+ 1 - 19
.github/workflows/bats-postgres.yml

@@ -10,7 +10,7 @@ jobs:
   build:
     strategy:
       matrix:
-        go-version: ["1.20.3"]
+        go-version: ["1.20.4"]
 
     name: "Build + tests"
     runs-on: ubuntu-latest
@@ -86,24 +86,6 @@ jobs:
         PGPASSWORD: "secret"
         PGUSER: postgres
 
-#    - name: "Build crowdsec and fixture (DB_BACKEND: postgres)"
-#      run: make clean bats-build bats-fixture
-#      env:
-#        DB_BACKEND: postgres
-#        PGHOST: 127.0.0.1
-#        PGPORT: 5432
-#        PGPASSWORD: "secret"
-#        PGUSER: postgres
-#
-#    - name: "Run tests (DB_BACKEND: postgres)"
-#      run: make bats-test
-#      env:
-#        DB_BACKEND: postgres
-#        PGHOST: 127.0.0.1
-#        PGPORT: 5432
-#        PGPASSWORD: "secret"
-#        PGUSER: postgres
-
     - name: "Show stack traces"
       run: for file in $(find /tmp/crowdsec-crash.*.txt); do echo ">>>>> $file"; cat $file; echo; done
       if: ${{ always() }}

+ 1 - 2
.github/workflows/bats-sqlite-coverage.yml

@@ -11,7 +11,7 @@ jobs:
   build:
     strategy:
       matrix:
-        go-version: ["1.20.3"]
+        go-version: ["1.20.4"]
 
     name: "Build + tests"
     runs-on: ubuntu-latest
@@ -72,7 +72,6 @@ jobs:
           -e '/plugins/notifications' \
           -e '/pkg/protobufs' \
           -e '/pkg/cwversions' \
-          -e '/pkg/cstest' \
           -e '/pkg/models' \
           < coverage-bats-raw.out \
           > coverage-bats.out

+ 1 - 1
.github/workflows/ci-windows-build-msi.yml

@@ -23,7 +23,7 @@ jobs:
   build:
     strategy:
       matrix:
-        go-version: ["1.20.3"]
+        go-version: ["1.20.4"]
 
     name: Build
     runs-on: windows-2019

+ 1 - 1
.github/workflows/go-tests-windows.yml

@@ -22,7 +22,7 @@ jobs:
   build:
     strategy:
       matrix:
-        go-version: ["1.20.3"]
+        go-version: ["1.20.4"]
 
     name: "Build + tests"
     runs-on: windows-2022

+ 1 - 1
.github/workflows/go-tests.yml

@@ -34,7 +34,7 @@ jobs:
   build:
     strategy:
       matrix:
-        go-version: ["1.20.3"]
+        go-version: ["1.20.4"]
 
     name: "Build + tests"
     runs-on: ubuntu-latest

+ 70 - 0
.github/workflows/publish_docker-image_on_master-debian.yml

@@ -0,0 +1,70 @@
+name: Publish Debian Docker image on Push to Master
+
+on:
+  push:
+    branches: [ master ]
+    paths:
+      - 'pkg/**'
+      - 'cmd/**'
+      - 'plugins/**'
+      - 'docker/docker_start.sh'
+      - 'docker/config.yaml'
+      - '.github/workflows/publish_docker-image_on_master-debian.yml'
+      - 'Dockerfile.debian'
+      - 'go.mod'
+      - 'go.sum'
+      - 'Makefile'
+
+jobs:
+  push_to_registry:
+    name: Push Debian Docker image to Docker Hub
+    runs-on: ubuntu-latest
+    steps:
+
+      - name: Check out the repo
+        uses: actions/checkout@v3
+        with:
+          fetch-depth: 0
+
+      - name: Prepare
+        id: prep
+        run: |
+          DOCKER_IMAGE=crowdsecurity/crowdsec
+          GHCR_IMAGE=ghcr.io/${{ github.repository_owner }}/crowdsec
+          VERSION=dev-debian
+          TAGS="${DOCKER_IMAGE}:${VERSION},${GHCR_IMAGE}:${VERSION}"
+          echo "tags=${TAGS}" >> $GITHUB_OUTPUT
+          echo "created=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
+
+      - name: Set up Docker Buildx
+        uses: docker/setup-buildx-action@v2
+        with:
+          config: .github/buildkit.toml
+
+      - name: Login to DockerHub
+        uses: docker/login-action@v2
+        with:
+          username: ${{ secrets.DOCKER_USERNAME }}
+          password: ${{ secrets.DOCKER_PASSWORD }}
+
+      - name: Login to GitHub Container Registry
+        uses: docker/login-action@v2
+        with:
+          registry: ghcr.io
+          username: ${{ github.repository_owner }}
+          password: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Build and push full image
+        uses: docker/build-push-action@v4
+        with:
+          context: .
+          file: ./Dockerfile.debian
+          push: ${{ github.event_name != 'pull_request' }}
+          tags: ${{ steps.prep.outputs.tags }}
+          platforms: linux/amd64
+          labels: |
+            org.opencontainers.image.source=${{ github.event.repository.html_url }}
+            org.opencontainers.image.created=${{ steps.prep.outputs.created }}
+            org.opencontainers.image.revision=${{ github.sha }}
+          cache-from: type=gha
+          cache-to: type=gha,mode=min

+ 8 - 4
.github/workflows/release_publish-package.yml

@@ -6,11 +6,15 @@ on:
     types:
       - prereleased
 
+permissions:
+  # Use write for: hub release edit
+  contents: write
+
 jobs:
   build:
     strategy:
       matrix:
-        go-version: ["1.20.3"]
+        go-version: ["1.20.4"]
 
     name: Build and upload binary package
     runs-on: ubuntu-latest
@@ -43,8 +47,8 @@ jobs:
         run: make release
 
       - name: Upload to release
-        uses: JasonEtco/upload-to-release@master
-        with:
-          args: crowdsec-release.tgz application/x-gzip
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        run: |
+          tag_name="${GITHUB_REF##*/}"
+          hub release edit -a crowdsec-release.tgz -m "" "$tag_name"

+ 2 - 2
.github/workflows/release_publish_docker-image-debian.yml

@@ -29,7 +29,7 @@ jobs:
             VERSION=pr-${{ github.event.number }}
           fi
           TAGS="${DOCKER_IMAGE}:${VERSION}-debian"
-          if [[ ${{ github.event.action }} == released ]]; then
+          if [[ "${{ github.event.action }}" == "released" ]]; then
             TAGS=$TAGS,${DOCKER_IMAGE}:latest-debian
           fi
           echo "version=${VERSION}" >> $GITHUB_OUTPUT
@@ -54,7 +54,7 @@ jobs:
           file: ./Dockerfile.debian
           push: ${{ github.event_name != 'pull_request' }}
           tags: ${{ steps.prep.outputs.tags }}
-          platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6,linux/386
+          platforms: linux/amd64,linux/arm64,linux/386
           labels: |
             org.opencontainers.image.source=${{ github.event.repository.html_url }}
             org.opencontainers.image.created=${{ steps.prep.outputs.created }}

+ 7 - 0
.gitignore

@@ -8,10 +8,17 @@
 .pc
 .vscode
 
+# If vendor is included, allow prebuilt (wasm?) libraries.
+!vendor/**/*.so
+
 # Test binaries, built with `go test -c`
 *.test
 *.cover
 
+# VMs used for dev/test
+
+.vagrant
+
 # Test binaries, built from *_test.go
 pkg/csplugin/tests/cs_plugin_test*
 

+ 12 - 2
Dockerfile

@@ -1,5 +1,5 @@
 # vim: set ft=dockerfile:
-ARG GOVERSION=1.20.3
+ARG GOVERSION=1.20.4
 
 FROM golang:${GOVERSION}-alpine AS build
 
@@ -7,9 +7,19 @@ WORKDIR /go/src/crowdsec
 
 COPY . .
 
+# Alpine does not ship a static version of re2, we can build it ourselves
+# Later versions require 'abseil', which is likewise not available in its static form
+ENV RE2_VERSION=2023-03-01
+
 # wizard.sh requires GNU coreutils
-RUN apk add --no-cache git gcc libc-dev make bash gettext binutils-gold coreutils && \
+RUN apk add --no-cache git g++ gcc libc-dev make bash gettext binutils-gold coreutils icu-static re2-dev pkgconfig && \
+    wget https://github.com/google/re2/archive/refs/tags/${RE2_VERSION}.tar.gz && \
+    tar -xzf ${RE2_VERSION}.tar.gz && \
+    cd re2-${RE2_VERSION} && \
+    make && \
+    make install && \
     echo "githubciXXXXXXXXXXXXXXXXXXXXXXXX" > /etc/machine-id && \
+    cd - && \
     make clean release DOCKER_BUILD=1 && \
     cd crowdsec-v* && \
     ./wizard.sh --docker-mode && \

+ 5 - 2
Dockerfile.debian

@@ -1,5 +1,5 @@
 # vim: set ft=dockerfile:
-ARG GOVERSION=1.20.3
+ARG GOVERSION=1.20.4
 
 FROM golang:${GOVERSION}-bullseye AS build
 
@@ -12,7 +12,7 @@ ENV DEBCONF_NOWARNINGS="yes"
 
 # wizard.sh requires GNU coreutils
 RUN apt-get update && \
-    apt-get install -y -q git gcc libc-dev make bash gettext binutils-gold coreutils tzdata && \
+    apt-get install -y -q git gcc libc-dev make bash gettext binutils-gold coreutils tzdata libre2-dev && \
     echo "githubciXXXXXXXXXXXXXXXXXXXXXXXX" > /etc/machine-id && \
     make clean release DOCKER_BUILD=1 && \
     cd crowdsec-v* && \
@@ -44,6 +44,9 @@ RUN apt-get update && \
     mkdir -p /staging/var/lib/crowdsec && \
     mkdir -p /var/lib/crowdsec/data
 
+RUN echo "deb http://deb.debian.org/debian bullseye-backports main" >> /etc/apt/sources.list \
+    && apt-get update && apt-get install -t bullseye-backports -y libsystemd0
+
 COPY --from=build /go/bin/yq /usr/local/bin/yq
 COPY --from=build /etc/crowdsec /staging/etc/crowdsec
 COPY --from=build /usr/local/bin/crowdsec /usr/local/bin/crowdsec

+ 76 - 71
Makefile

@@ -1,36 +1,42 @@
 include mk/platform.mk
 
-CROWDSEC_FOLDER = ./cmd/crowdsec
-CSCLI_FOLDER = ./cmd/crowdsec-cli/
+BUILD_REQUIRE_GO_MAJOR ?= 1
+BUILD_REQUIRE_GO_MINOR ?= 20
 
-HTTP_PLUGIN_FOLDER = ./plugins/notifications/http
-SLACK_PLUGIN_FOLDER = ./plugins/notifications/slack
-SPLUNK_PLUGIN_FOLDER = ./plugins/notifications/splunk
-EMAIL_PLUGIN_FOLDER = ./plugins/notifications/email
-DUMMY_PLUGIN_FOLDER = ./plugins/notifications/dummy
+GOCMD = go
+GOTEST = $(GOCMD) test
 
-HTTP_PLUGIN_BIN = notification-http$(EXT)
-SLACK_PLUGIN_BIN = notification-slack$(EXT)
-SPLUNK_PLUGIN_BIN = notification-splunk$(EXT)
-EMAIL_PLUGIN_BIN = notification-email$(EXT)
-DUMMY_PLUGIN_BIN= notification-dummy$(EXT)
+BUILD_CODENAME ?= alphaga
 
-HTTP_PLUGIN_CONFIG = http.yaml
-SLACK_PLUGIN_CONFIG = slack.yaml
-SPLUNK_PLUGIN_CONFIG = splunk.yaml
-EMAIL_PLUGIN_CONFIG = email.yaml
+CROWDSEC_FOLDER = ./cmd/crowdsec
+CSCLI_FOLDER = ./cmd/crowdsec-cli/
+
+PLUGINS ?= $(patsubst ./plugins/notifications/%,%,$(wildcard ./plugins/notifications/*))
+PLUGINS_DIR = ./plugins/notifications
 
 CROWDSEC_BIN = crowdsec$(EXT)
 CSCLI_BIN = cscli$(EXT)
-BUILD_CMD = build
+
+# Directory for the release files
+RELDIR = crowdsec-$(BUILD_VERSION)
 
 GO_MODULE_NAME = github.com/crowdsecurity/crowdsec
 
+# see if we have libre2-dev installed for C++ optimizations
+RE2_CHECK := $(shell pkg-config --libs re2 2>/dev/null)
+
+#--------------------------------------
+#
+# Define MAKE_FLAGS and LD_OPTS for the sub-makefiles in cmd/ and plugins/
+#
+
+MAKE_FLAGS = --no-print-directory GOARCH=$(GOARCH) GOOS=$(GOOS) RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
+
 LD_OPTS_VARS= \
--X '$(GO_MODULE_NAME)/pkg/cwversion.Version=$(BUILD_VERSION)' \
--X '$(GO_MODULE_NAME)/pkg/cwversion.BuildDate=$(BUILD_TIMESTAMP)' \
+-X 'github.com/crowdsecurity/go-cs-lib/pkg/version.Version=$(BUILD_VERSION)' \
+-X 'github.com/crowdsecurity/go-cs-lib/pkg/version.BuildDate=$(BUILD_TIMESTAMP)' \
+-X 'github.com/crowdsecurity/go-cs-lib/pkg/version.Tag=$(BUILD_TAG)' \
 -X '$(GO_MODULE_NAME)/pkg/cwversion.Codename=$(BUILD_CODENAME)' \
--X '$(GO_MODULE_NAME)/pkg/cwversion.Tag=$(BUILD_TAG)' \
 -X '$(GO_MODULE_NAME)/pkg/csconfig.defaultConfigDir=$(DEFAULT_CONFIGDIR)' \
 -X '$(GO_MODULE_NAME)/pkg/csconfig.defaultDataDir=$(DEFAULT_DATADIR)'
 
@@ -38,66 +44,67 @@ ifneq (,$(DOCKER_BUILD))
 LD_OPTS_VARS += -X '$(GO_MODULE_NAME)/pkg/cwversion.System=docker'
 endif
 
-ifdef BUILD_STATIC
-$(warning WARNING: The BUILD_STATIC variable is deprecated and has no effect. Builds are static by default since v1.5.0.)
+GO_TAGS := netgo,osusergo,sqlite_omit_load_extension
+
+ifneq (,$(RE2_CHECK))
+# += adds a space that we don't want
+GO_TAGS := $(GO_TAGS),re2_cgo
+LD_OPTS_VARS += -X '$(GO_MODULE_NAME)/pkg/cwversion.Libre2=C++'
 endif
 
 export LD_OPTS=-ldflags "-s -w -extldflags '-static' $(LD_OPTS_VARS)" \
-	-trimpath -tags netgo,osusergo,sqlite_omit_load_extension
+	-trimpath -tags $(GO_TAGS)
 
 ifneq (,$(TEST_COVERAGE))
 LD_OPTS += -cover
 endif
 
-GOCMD = go
-GOTEST = $(GOCMD) test
-
-RELDIR = crowdsec-$(BUILD_VERSION)
+#--------------------------------------
 
 .PHONY: build
-build: goversion crowdsec cscli plugins
+build: pre-build goversion crowdsec cscli plugins
+
+.PHONY: pre-build
+pre-build:
+ifdef BUILD_STATIC
+	$(warning WARNING: The BUILD_STATIC variable is deprecated and has no effect. Builds are static by default since v1.5.0.)
+endif
+	$(info Building $(BUILD_VERSION) ($(BUILD_TAG)) for $(GOOS)/$(GOARCH))
+ifneq (,$(RE2_CHECK))
+	$(info Using C++ regexp library)
+else
+	$(info Fallback to WebAssembly regexp library. To use the C++ version, make sure you have installed libre2-dev and pkg-config.)
+endif
+	$(info )
 
 .PHONY: all
 all: clean test build
 
 .PHONY: plugins
-plugins: http-plugin slack-plugin splunk-plugin email-plugin dummy-plugin
+plugins:
+	@$(foreach plugin,$(PLUGINS), \
+		$(MAKE) -C $(PLUGINS_DIR)/$(plugin) build $(MAKE_FLAGS); \
+	)
 
 .PHONY: clean
 clean: testclean
-	@$(MAKE) -C $(CROWDSEC_FOLDER) clean --no-print-directory RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
-	@$(MAKE) -C $(CSCLI_FOLDER) clean --no-print-directory RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
+	@$(MAKE) -C $(CROWDSEC_FOLDER) clean $(MAKE_FLAGS)
+	@$(MAKE) -C $(CSCLI_FOLDER) clean $(MAKE_FLAGS)
 	@$(RM) $(CROWDSEC_BIN) $(WIN_IGNORE_ERR)
 	@$(RM) $(CSCLI_BIN) $(WIN_IGNORE_ERR)
 	@$(RM) *.log $(WIN_IGNORE_ERR)
 	@$(RM) crowdsec-release.tgz $(WIN_IGNORE_ERR)
-	@$(RM) $(HTTP_PLUGIN_FOLDER)/$(HTTP_PLUGIN_BIN) $(WIN_IGNORE_ERR)
-	@$(RM) $(SLACK_PLUGIN_FOLDER)/$(SLACK_PLUGIN_BIN) $(WIN_IGNORE_ERR)
-	@$(RM) $(SPLUNK_PLUGIN_FOLDER)/$(SPLUNK_PLUGIN_BIN) $(WIN_IGNORE_ERR)
-	@$(RM) $(EMAIL_PLUGIN_FOLDER)/$(EMAIL_PLUGIN_BIN) $(WIN_IGNORE_ERR)
-	@$(RM) $(DUMMY_PLUGIN_FOLDER)/$(DUMMY_PLUGIN_BIN) $(WIN_IGNORE_ERR)
-
+	@$(foreach plugin,$(PLUGINS), \
+		$(MAKE) -C $(PLUGINS_DIR)/$(plugin) clean $(MAKE_FLAGS); \
+	)
 
+.PHONY: cscli
 cscli: goversion
-	@$(MAKE) -C $(CSCLI_FOLDER) build --no-print-directory GOARCH=$(GOARCH) GOOS=$(GOOS) RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
+	@$(MAKE) -C $(CSCLI_FOLDER) build $(MAKE_FLAGS)
 
+.PHONY: crowdsec
 crowdsec: goversion
-	@$(MAKE) -C $(CROWDSEC_FOLDER) build --no-print-directory GOARCH=$(GOARCH) GOOS=$(GOOS) RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
-
-http-plugin: goversion
-	@$(MAKE) -C $(HTTP_PLUGIN_FOLDER) build --no-print-directory GOARCH=$(GOARCH) GOOS=$(GOOS) RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
-
-slack-plugin: goversion
-	@$(MAKE) -C $(SLACK_PLUGIN_FOLDER) build --no-print-directory GOARCH=$(GOARCH) GOOS=$(GOOS) RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
-
-splunk-plugin: goversion
-	@$(MAKE) -C $(SPLUNK_PLUGIN_FOLDER) build --no-print-directory GOARCH=$(GOARCH) GOOS=$(GOOS) RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
-
-email-plugin: goversion
-	@$(MAKE) -C $(EMAIL_PLUGIN_FOLDER) build --no-print-directory GOARCH=$(GOARCH) GOOS=$(GOOS) RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
-
-dummy-plugin: goversion
-	$(MAKE) -C $(DUMMY_PLUGIN_FOLDER) build --no-print-directory GOARCH=$(GOARCH) GOOS=$(GOOS) RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
+	@$(MAKE) -C $(CROWDSEC_FOLDER) build $(MAKE_FLAGS)
 
 .PHONY: testclean
 testclean: bats-clean
@@ -130,35 +137,33 @@ localstack:
 localstack-stop:
 	docker-compose -f test/localstack/docker-compose.yml down
 
-package-common:
+.PHONY: vendor
+vendor:
+	@echo "Vendoring dependencies"
+	@$(GOCMD) mod vendor
+	@$(foreach plugin,$(PLUGINS), \
+		$(MAKE) -C $(PLUGINS_DIR)/$(plugin) vendor $(MAKE_FLAGS); \
+	)
+
+.PHONY: package
+package:
 	@echo "Building Release to dir $(RELDIR)"
 	@$(MKDIR) $(RELDIR)/cmd/crowdsec
 	@$(MKDIR) $(RELDIR)/cmd/crowdsec-cli
-	@$(MKDIR) $(RELDIR)/$(subst ./,,$(HTTP_PLUGIN_FOLDER))
-	@$(MKDIR) $(RELDIR)/$(subst ./,,$(SLACK_PLUGIN_FOLDER))
-	@$(MKDIR) $(RELDIR)/$(subst ./,,$(SPLUNK_PLUGIN_FOLDER))
-	@$(MKDIR) $(RELDIR)/$(subst ./,,$(EMAIL_PLUGIN_FOLDER))
-
 	@$(CP) $(CROWDSEC_FOLDER)/$(CROWDSEC_BIN) $(RELDIR)/cmd/crowdsec
 	@$(CP) $(CSCLI_FOLDER)/$(CSCLI_BIN) $(RELDIR)/cmd/crowdsec-cli
 
-	@$(CP) $(HTTP_PLUGIN_FOLDER)/$(HTTP_PLUGIN_BIN) $(RELDIR)/$(subst ./,,$(HTTP_PLUGIN_FOLDER))
-	@$(CP) $(SLACK_PLUGIN_FOLDER)/$(SLACK_PLUGIN_BIN) $(RELDIR)/$(subst ./,,$(SLACK_PLUGIN_FOLDER))
-	@$(CP) $(SPLUNK_PLUGIN_FOLDER)/$(SPLUNK_PLUGIN_BIN) $(RELDIR)/$(subst ./,,$(SPLUNK_PLUGIN_FOLDER))
-	@$(CP) $(EMAIL_PLUGIN_FOLDER)/$(EMAIL_PLUGIN_BIN) $(RELDIR)/$(subst ./,,$(EMAIL_PLUGIN_FOLDER))
-
-	@$(CP) $(HTTP_PLUGIN_FOLDER)/$(HTTP_PLUGIN_CONFIG) $(RELDIR)/$(subst ./,,$(HTTP_PLUGIN_FOLDER))
-	@$(CP) $(SLACK_PLUGIN_FOLDER)/$(SLACK_PLUGIN_CONFIG) $(RELDIR)/$(subst ./,,$(SLACK_PLUGIN_FOLDER))
-	@$(CP) $(SPLUNK_PLUGIN_FOLDER)/$(SPLUNK_PLUGIN_CONFIG) $(RELDIR)/$(subst ./,,$(SPLUNK_PLUGIN_FOLDER))
-	@$(CP) $(EMAIL_PLUGIN_FOLDER)/$(EMAIL_PLUGIN_CONFIG) $(RELDIR)/$(subst ./,,$(EMAIL_PLUGIN_FOLDER))
+	@$(foreach plugin,$(PLUGINS), \
+		$(MKDIR) $(RELDIR)/$(PLUGINS_DIR)/$(plugin); \
+		$(CP) $(PLUGINS_DIR)/$(plugin)/notification-$(plugin)$(EXT) $(RELDIR)/$(PLUGINS_DIR)/$(plugin); \
+		$(CP) $(PLUGINS_DIR)/$(plugin)/$(plugin).yaml $(RELDIR)/$(PLUGINS_DIR)/$(plugin)/; \
+	)
 
 	@$(CPR) ./config $(RELDIR)
 	@$(CP) wizard.sh $(RELDIR)
 	@$(CP) scripts/test_env.sh $(RELDIR)
 	@$(CP) scripts/test_env.ps1 $(RELDIR)
 
-.PHONY: package
-package: package-common
 	@tar cvzf crowdsec-release.tgz $(RELDIR)
 
 .PHONY: check_release

+ 1 - 1
azure-pipelines.yml

@@ -27,7 +27,7 @@ stages:
           - task: GoTool@0
             displayName: "Install Go 1.20"
             inputs:
-                version: '1.20.3'
+                version: '1.20.4'
 
           - pwsh: |
               choco install -y make

+ 0 - 2
cmd/crowdsec-cli/Makefile

@@ -7,9 +7,7 @@ endif
 # Go parameters
 GOCMD = go
 GOBUILD = $(GOCMD) build
-GOCLEAN = $(GOCMD) clean
 GOTEST = $(GOCMD) test
-GOGET = $(GOCMD) get
 
 BINARY_NAME = cscli$(EXT)
 PREFIX ?= "/"

+ 3 - 2
cmd/crowdsec-cli/alerts.go

@@ -20,8 +20,9 @@ import (
 	"github.com/spf13/cobra"
 	"gopkg.in/yaml.v2"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/version"
+
 	"github.com/crowdsecurity/crowdsec/pkg/apiclient"
-	"github.com/crowdsecurity/crowdsec/pkg/cwversion"
 	"github.com/crowdsecurity/crowdsec/pkg/database"
 	"github.com/crowdsecurity/crowdsec/pkg/models"
 	"github.com/crowdsecurity/crowdsec/pkg/types"
@@ -219,7 +220,7 @@ func NewAlertsCmd() *cobra.Command {
 			Client, err = apiclient.NewClient(&apiclient.Config{
 				MachineID:     csConfig.API.Client.Credentials.Login,
 				Password:      strfmt.Password(csConfig.API.Client.Credentials.Password),
-				UserAgent:     fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+				UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
 				URL:           apiURL,
 				VersionPrefix: "v1",
 			})

+ 3 - 5
cmd/crowdsec-cli/bouncers.go

@@ -11,6 +11,7 @@ import (
 	"github.com/fatih/color"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
+	"golang.org/x/exp/slices"
 
 	middlewares "github.com/crowdsecurity/crowdsec/pkg/apiserver/middlewares/v1"
 	"github.com/crowdsecurity/crowdsec/pkg/database"
@@ -122,7 +123,6 @@ func runBouncersAdd(cmd *cobra.Command, args []string) error {
 	return nil
 }
 
-
 func NewBouncersAddCmd() *cobra.Command {
 	cmdBouncersAdd := &cobra.Command{
 		Use:   "add MyBouncerName [--length 16]",
@@ -133,7 +133,7 @@ cscli bouncers add MyBouncerName -l 24
 cscli bouncers add MyBouncerName -k <random-key>`,
 		Args:              cobra.ExactArgs(1),
 		DisableAutoGenTag: true,
-		RunE: runBouncersAdd,
+		RunE:              runBouncersAdd,
 	}
 
 	flags := cmdBouncersAdd.Flags()
@@ -144,7 +144,6 @@ cscli bouncers add MyBouncerName -k <random-key>`,
 	return cmdBouncersAdd
 }
 
-
 func runBouncersDelete(cmd *cobra.Command, args []string) error {
 	for _, bouncerID := range args {
 		err := dbClient.DeleteBouncer(bouncerID)
@@ -157,7 +156,6 @@ func runBouncersDelete(cmd *cobra.Command, args []string) error {
 	return nil
 }
 
-
 func NewBouncersDeleteCmd() *cobra.Command {
 	cmdBouncersDelete := &cobra.Command{
 		Use:               "delete MyBouncerName",
@@ -178,7 +176,7 @@ func NewBouncersDeleteCmd() *cobra.Command {
 			}
 			ret := make([]string, 0)
 			for _, bouncer := range bouncers {
-				if strings.Contains(bouncer.Name, toComplete) && !inSlice(bouncer.Name, args) {
+				if strings.Contains(bouncer.Name, toComplete) && !slices.Contains(args, bouncer.Name) {
 					ret = append(ret, bouncer.Name)
 				}
 			}

+ 4 - 3
cmd/crowdsec-cli/capi.go

@@ -6,10 +6,11 @@ import (
 	"net/url"
 	"os"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/version"
+
 	"github.com/crowdsecurity/crowdsec/pkg/apiclient"
 	"github.com/crowdsecurity/crowdsec/pkg/csconfig"
 	"github.com/crowdsecurity/crowdsec/pkg/cwhub"
-	"github.com/crowdsecurity/crowdsec/pkg/cwversion"
 	"github.com/crowdsecurity/crowdsec/pkg/fflag"
 	"github.com/crowdsecurity/crowdsec/pkg/models"
 	"github.com/crowdsecurity/crowdsec/pkg/types"
@@ -70,7 +71,7 @@ func NewCapiRegisterCmd() *cobra.Command {
 			_, err = apiclient.RegisterClient(&apiclient.Config{
 				MachineID:     capiUser,
 				Password:      password,
-				UserAgent:     fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+				UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
 				URL:           apiurl,
 				VersionPrefix: CAPIURLPrefix,
 			}, nil)
@@ -164,7 +165,7 @@ func NewCapiStatusCmd() *cobra.Command {
 				log.Fatalf("no scenarios installed, abort")
 			}
 
-			Client, err = apiclient.NewDefaultClient(apiurl, CAPIURLPrefix, fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()), nil)
+			Client, err = apiclient.NewDefaultClient(apiurl, CAPIURLPrefix, fmt.Sprintf("crowdsec/%s", version.String()), nil)
 			if err != nil {
 				log.Fatalf("init default client: %s", err)
 			}

+ 1 - 0
cmd/crowdsec-cli/config.go

@@ -13,6 +13,7 @@ func NewConfigCmd() *cobra.Command {
 	}
 
 	cmdConfig.AddCommand(NewConfigShowCmd())
+	cmdConfig.AddCommand(NewConfigShowYAMLCmd())
 	cmdConfig.AddCommand(NewConfigBackupCmd())
 	cmdConfig.AddCommand(NewConfigRestoreCmd())
 	cmdConfig.AddCommand(NewConfigFeatureFlagsCmd())

+ 24 - 0
cmd/crowdsec-cli/config_showyaml.go

@@ -0,0 +1,24 @@
+package main
+
+import (
+	"fmt"
+
+	"github.com/spf13/cobra"
+)
+
+func runConfigShowYAML(cmd *cobra.Command, args []string) error {
+	fmt.Println(mergedConfig)
+	return nil
+}
+
+func NewConfigShowYAMLCmd() *cobra.Command {
+	cmdConfigShow := &cobra.Command{
+		Use:               "show-yaml",
+		Short:             "Displays merged config.yaml + config.yaml.local",
+		Args:              cobra.ExactArgs(0),
+		DisableAutoGenTag: true,
+		RunE:              runConfigShowYAML,
+	}
+
+	return cmdConfigShow
+}

+ 9 - 7
cmd/crowdsec-cli/console.go

@@ -16,10 +16,12 @@ import (
 	"github.com/spf13/cobra"
 	"gopkg.in/yaml.v3"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/ptr"
+	"github.com/crowdsecurity/go-cs-lib/pkg/version"
+
 	"github.com/crowdsecurity/crowdsec/pkg/apiclient"
 	"github.com/crowdsecurity/crowdsec/pkg/csconfig"
 	"github.com/crowdsecurity/crowdsec/pkg/cwhub"
-	"github.com/crowdsecurity/crowdsec/pkg/cwversion"
 	"github.com/crowdsecurity/crowdsec/pkg/fflag"
 	"github.com/crowdsecurity/crowdsec/pkg/types"
 )
@@ -101,7 +103,7 @@ After running this command your will need to validate the enrollment in the weba
 				MachineID:     csConfig.API.Server.OnlineClient.Credentials.Login,
 				Password:      password,
 				Scenarios:     scenarios,
-				UserAgent:     fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+				UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
 				URL:           apiURL,
 				VersionPrefix: "v3",
 			})
@@ -249,7 +251,7 @@ func SetConsoleOpts(args []string, wanted bool) {
 				}
 			} else {
 				log.Infof("%s set to %t", csconfig.CONSOLE_MANAGEMENT, wanted)
-				csConfig.API.Server.ConsoleConfig.ConsoleManagement = types.BoolPtr(wanted)
+				csConfig.API.Server.ConsoleConfig.ConsoleManagement = ptr.Of(wanted)
 			}
 			if csConfig.API.Server.OnlineClient.Credentials != nil {
 				changed := false
@@ -283,7 +285,7 @@ func SetConsoleOpts(args []string, wanted bool) {
 				}
 			} else {
 				log.Infof("%s set to %t", csconfig.SEND_CUSTOM_SCENARIOS, wanted)
-				csConfig.API.Server.ConsoleConfig.ShareCustomScenarios = types.BoolPtr(wanted)
+				csConfig.API.Server.ConsoleConfig.ShareCustomScenarios = ptr.Of(wanted)
 			}
 		case csconfig.SEND_TAINTED_SCENARIOS:
 			/*for each flag check if it's already set before setting it*/
@@ -296,7 +298,7 @@ func SetConsoleOpts(args []string, wanted bool) {
 				}
 			} else {
 				log.Infof("%s set to %t", csconfig.SEND_TAINTED_SCENARIOS, wanted)
-				csConfig.API.Server.ConsoleConfig.ShareTaintedScenarios = types.BoolPtr(wanted)
+				csConfig.API.Server.ConsoleConfig.ShareTaintedScenarios = ptr.Of(wanted)
 			}
 		case csconfig.SEND_MANUAL_SCENARIOS:
 			/*for each flag check if it's already set before setting it*/
@@ -309,7 +311,7 @@ func SetConsoleOpts(args []string, wanted bool) {
 				}
 			} else {
 				log.Infof("%s set to %t", csconfig.SEND_MANUAL_SCENARIOS, wanted)
-				csConfig.API.Server.ConsoleConfig.ShareManualDecisions = types.BoolPtr(wanted)
+				csConfig.API.Server.ConsoleConfig.ShareManualDecisions = ptr.Of(wanted)
 			}
 		case csconfig.SEND_CONTEXT:
 			/*for each flag check if it's already set before setting it*/
@@ -322,7 +324,7 @@ func SetConsoleOpts(args []string, wanted bool) {
 				}
 			} else {
 				log.Infof("%s set to %t", csconfig.SEND_CONTEXT, wanted)
-				csConfig.API.Server.ConsoleConfig.ShareContext = types.BoolPtr(wanted)
+				csConfig.API.Server.ConsoleConfig.ShareContext = ptr.Of(wanted)
 			}
 		default:
 			log.Fatalf("unknown flag %s", arg)

+ 64 - 28
cmd/crowdsec-cli/decisions.go

@@ -19,8 +19,10 @@ import (
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/ptr"
+	"github.com/crowdsecurity/go-cs-lib/pkg/version"
+
 	"github.com/crowdsecurity/crowdsec/pkg/apiclient"
-	"github.com/crowdsecurity/crowdsec/pkg/cwversion"
 	"github.com/crowdsecurity/crowdsec/pkg/models"
 	"github.com/crowdsecurity/crowdsec/pkg/types"
 )
@@ -120,7 +122,7 @@ func NewDecisionsCmd() *cobra.Command {
 			Client, err = apiclient.NewClient(&apiclient.Config{
 				MachineID:     csConfig.API.Client.Credentials.Login,
 				Password:      password,
-				UserAgent:     fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+				UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
 				URL:           apiurl,
 				VersionPrefix: "v1",
 			})
@@ -488,6 +490,7 @@ func NewDecisionsImportCmd() *cobra.Command {
 		importReason    string
 		importType      string
 		importFile      string
+		batchSize       int
 	)
 
 	var cmdDecisionImport = &cobra.Command{
@@ -578,37 +581,69 @@ decisions.json :
 					log.Debugf("'scope' line %d, using supplied value: '%s'", line, importScope)
 				}
 				decision := models.Decision{
-					Value:     types.StrPtr(decisionLine.Value),
-					Duration:  types.StrPtr(decisionLine.Duration),
-					Origin:    types.StrPtr(decisionLine.Origin),
-					Scenario:  types.StrPtr(decisionLine.Scenario),
-					Type:      types.StrPtr(decisionLine.Type),
-					Scope:     types.StrPtr(decisionLine.Scope),
+					Value:     ptr.Of(decisionLine.Value),
+					Duration:  ptr.Of(decisionLine.Duration),
+					Origin:    ptr.Of(decisionLine.Origin),
+					Scenario:  ptr.Of(decisionLine.Scenario),
+					Type:      ptr.Of(decisionLine.Type),
+					Scope:     ptr.Of(decisionLine.Scope),
 					Simulated: new(bool),
 				}
 				decisionsList = append(decisionsList, &decision)
 			}
 			alerts := models.AddAlertsRequest{}
-			importAlert := models.Alert{
-				CreatedAt: time.Now().UTC().Format(time.RFC3339),
-				Scenario:  types.StrPtr(fmt.Sprintf("import %s : %d IPs", importFile, len(decisionsList))),
-				Message:   types.StrPtr(""),
-				Events:    []*models.Event{},
-				Source: &models.Source{
-					Scope: types.StrPtr(""),
-					Value: types.StrPtr(""),
-				},
-				StartAt:         types.StrPtr(time.Now().UTC().Format(time.RFC3339)),
-				StopAt:          types.StrPtr(time.Now().UTC().Format(time.RFC3339)),
-				Capacity:        types.Int32Ptr(0),
-				Simulated:       types.BoolPtr(false),
-				EventsCount:     types.Int32Ptr(int32(len(decisionsList))),
-				Leakspeed:       types.StrPtr(""),
-				ScenarioHash:    types.StrPtr(""),
-				ScenarioVersion: types.StrPtr(""),
-				Decisions:       decisionsList,
-			}
-			alerts = append(alerts, &importAlert)
+
+			if batchSize > 0 {
+				for i := 0; i < len(decisionsList); i += batchSize {
+					end := i + batchSize
+					if end > len(decisionsList) {
+						end = len(decisionsList)
+					}
+					decisionBatch := decisionsList[i:end]
+					importAlert := models.Alert{
+						CreatedAt: time.Now().UTC().Format(time.RFC3339),
+						Scenario:  ptr.Of(fmt.Sprintf("import %s : %d IPs", importFile, len(decisionBatch))),
+
+						Message: ptr.Of(""),
+						Events:  []*models.Event{},
+						Source: &models.Source{
+							Scope: ptr.Of(""),
+							Value: ptr.Of(""),
+						},
+						StartAt:         ptr.Of(time.Now().UTC().Format(time.RFC3339)),
+						StopAt:          ptr.Of(time.Now().UTC().Format(time.RFC3339)),
+						Capacity:        ptr.Of(int32(0)),
+						Simulated:       ptr.Of(false),
+						EventsCount:     ptr.Of(int32(len(decisionBatch))),
+						Leakspeed:       ptr.Of(""),
+						ScenarioHash:    ptr.Of(""),
+						ScenarioVersion: ptr.Of(""),
+						Decisions:       decisionBatch,
+					}
+					alerts = append(alerts, &importAlert)
+				}
+			} else {
+				importAlert := models.Alert{
+					CreatedAt: time.Now().UTC().Format(time.RFC3339),
+					Scenario:  ptr.Of(fmt.Sprintf("import %s : %d IPs", importFile, len(decisionsList))),
+					Message:   ptr.Of(""),
+					Events:    []*models.Event{},
+					Source: &models.Source{
+						Scope: ptr.Of(""),
+						Value: ptr.Of(""),
+					},
+					StartAt:         ptr.Of(time.Now().UTC().Format(time.RFC3339)),
+					StopAt:          ptr.Of(time.Now().UTC().Format(time.RFC3339)),
+					Capacity:        ptr.Of(int32(0)),
+					Simulated:       ptr.Of(false),
+					EventsCount:     ptr.Of(int32(len(decisionsList))),
+					Leakspeed:       ptr.Of(""),
+					ScenarioHash:    ptr.Of(""),
+					ScenarioVersion: ptr.Of(""),
+					Decisions:       decisionsList,
+				}
+				alerts = append(alerts, &importAlert)
+			}
 
 			if len(decisionsList) > 1000 {
 				log.Infof("You are about to add %d decisions, this may take a while", len(decisionsList))
@@ -628,6 +663,7 @@ decisions.json :
 	cmdDecisionImport.Flags().StringVar(&importScope, "scope", types.Ip, "Decision scope (ie. ip,range,username)")
 	cmdDecisionImport.Flags().StringVarP(&importReason, "reason", "R", "", "Decision reason (ie. scenario-name)")
 	cmdDecisionImport.Flags().StringVarP(&importType, "type", "t", "", "Decision type (ie. ban,captcha,throttle)")
+	cmdDecisionImport.Flags().IntVar(&batchSize, "batch", 0, "Split import in batches of N decisions")
 
 	return cmdDecisionImport
 }

+ 10 - 1
cmd/crowdsec-cli/hub.go

@@ -1,6 +1,7 @@
 package main
 
 import (
+	"errors"
 	"fmt"
 
 	"github.com/fatih/color"
@@ -98,7 +99,15 @@ Fetches the [.index.json](https://github.com/crowdsecurity/hub/blob/master/.inde
 				log.Fatal(err)
 			}
 			if err := cwhub.UpdateHubIdx(csConfig.Hub); err != nil {
-				log.Fatalf("Failed to get Hub index : %v", err)
+				if errors.Is(err, cwhub.ErrIndexNotFound) {
+					log.Warnf("Could not find index file for branch '%s', using 'master'", cwhub.HubBranch)
+					cwhub.HubBranch = "master"
+					if err := cwhub.UpdateHubIdx(csConfig.Hub); err != nil {
+						log.Fatalf("Failed to get Hub index after retry : %v", err)
+					}
+				} else {
+					log.Fatalf("Failed to get Hub index : %v", err)
+				}
 			}
 			//use LocalSync to get warnings about tainted / outdated items
 			_, warn := cwhub.LocalSync(csConfig.Hub)

+ 20 - 18
cmd/crowdsec-cli/lapi.go

@@ -12,13 +12,15 @@ import (
 	"github.com/pkg/errors"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
+	"golang.org/x/exp/slices"
 	"gopkg.in/yaml.v2"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/version"
+
 	"github.com/crowdsecurity/crowdsec/pkg/alertcontext"
 	"github.com/crowdsecurity/crowdsec/pkg/apiclient"
 	"github.com/crowdsecurity/crowdsec/pkg/csconfig"
 	"github.com/crowdsecurity/crowdsec/pkg/cwhub"
-	"github.com/crowdsecurity/crowdsec/pkg/cwversion"
 	"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
 	"github.com/crowdsecurity/crowdsec/pkg/models"
 	"github.com/crowdsecurity/crowdsec/pkg/parser"
@@ -51,7 +53,7 @@ func runLapiStatus(cmd *cobra.Command, args []string) error {
 
 	Client, err = apiclient.NewDefaultClient(apiurl,
 		LAPIURLPrefix,
-		fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+		fmt.Sprintf("crowdsec/%s", version.String()),
 		nil)
 	if err != nil {
 		log.Fatalf("init default client: %s", err)
@@ -122,7 +124,7 @@ func runLapiRegister(cmd *cobra.Command, args []string) error {
 	_, err = apiclient.RegisterClient(&apiclient.Config{
 		MachineID:     lapiUser,
 		Password:      password,
-		UserAgent:     fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+		UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
 		URL:           apiurl,
 		VersionPrefix: LAPIURLPrefix,
 	}, nil)
@@ -258,7 +260,7 @@ cscli lapi context add --key file_source --value evt.Line.Src
 			}
 			data := csConfig.Crowdsec.ContextToSend[keyToAdd]
 			for _, val := range valuesToAdd {
-				if !inSlice(val, data) {
+				if !slices.Contains(data, val) {
 					log.Infof("value '%s' added to key '%s'", val, keyToAdd)
 					data = append(data, val)
 				}
@@ -332,7 +334,7 @@ cscli lapi context detect crowdsecurity/sshd-logs
 
 			fieldByParsers := make(map[string][]string)
 			for _, node := range csParsers.Nodes {
-				if !detectAll && !inSlice(node.Name, args) {
+				if !detectAll && !slices.Contains(args, node.Name) {
 					continue
 				}
 				if !detectAll {
@@ -343,7 +345,7 @@ cscli lapi context detect crowdsecurity/sshd-logs
 
 				subNodeFields := detectSubNode(node, *csParsers.Ctx)
 				for _, field := range subNodeFields {
-					if !inSlice(field, fieldByParsers[node.Name]) {
+					if !slices.Contains(fieldByParsers[node.Name], field) {
 						fieldByParsers[node.Name] = append(fieldByParsers[node.Name], field)
 					}
 				}
@@ -411,7 +413,7 @@ cscli lapi context delete --value evt.Line.Src
 			for _, value := range valuesToDelete {
 				valueFound := false
 				for key, context := range csConfig.Crowdsec.ContextToSend {
-					if inSlice(value, context) {
+					if slices.Contains(context, value) {
 						valueFound = true
 						csConfig.Crowdsec.ContextToSend[key] = removeFromSlice(value, context)
 						log.Infof("value '%s' has been removed from key '%s'", value, key)
@@ -443,13 +445,13 @@ func detectStaticField(GrokStatics []types.ExtraField) []string {
 	for _, static := range GrokStatics {
 		if static.Parsed != "" {
 			fieldName := fmt.Sprintf("evt.Parsed.%s", static.Parsed)
-			if !inSlice(fieldName, ret) {
+			if !slices.Contains(ret, fieldName) {
 				ret = append(ret, fieldName)
 			}
 		}
 		if static.Meta != "" {
 			fieldName := fmt.Sprintf("evt.Meta.%s", static.Meta)
-			if !inSlice(fieldName, ret) {
+			if !slices.Contains(ret, fieldName) {
 				ret = append(ret, fieldName)
 			}
 		}
@@ -458,7 +460,7 @@ func detectStaticField(GrokStatics []types.ExtraField) []string {
 			if !strings.HasPrefix(fieldName, "evt.") {
 				fieldName = "evt." + fieldName
 			}
-			if !inSlice(fieldName, ret) {
+			if !slices.Contains(ret, fieldName) {
 				ret = append(ret, fieldName)
 			}
 		}
@@ -472,7 +474,7 @@ func detectNode(node parser.Node, parserCTX parser.UnixParserCtx) []string {
 	if node.Grok.RunTimeRegexp != nil {
 		for _, capturedField := range node.Grok.RunTimeRegexp.Names() {
 			fieldName := fmt.Sprintf("evt.Parsed.%s", capturedField)
-			if !inSlice(fieldName, ret) {
+			if !slices.Contains(ret, fieldName) {
 				ret = append(ret, fieldName)
 			}
 		}
@@ -485,7 +487,7 @@ func detectNode(node parser.Node, parserCTX parser.UnixParserCtx) []string {
 		}
 		for _, capturedField := range grokCompiled.Names() {
 			fieldName := fmt.Sprintf("evt.Parsed.%s", capturedField)
-			if !inSlice(fieldName, ret) {
+			if !slices.Contains(ret, fieldName) {
 				ret = append(ret, fieldName)
 			}
 		}
@@ -494,7 +496,7 @@ func detectNode(node parser.Node, parserCTX parser.UnixParserCtx) []string {
 	if len(node.Grok.Statics) > 0 {
 		staticsField := detectStaticField(node.Grok.Statics)
 		for _, staticField := range staticsField {
-			if !inSlice(staticField, ret) {
+			if !slices.Contains(ret, staticField) {
 				ret = append(ret, staticField)
 			}
 		}
@@ -503,7 +505,7 @@ func detectNode(node parser.Node, parserCTX parser.UnixParserCtx) []string {
 	if len(node.Statics) > 0 {
 		staticsField := detectStaticField(node.Statics)
 		for _, staticField := range staticsField {
-			if !inSlice(staticField, ret) {
+			if !slices.Contains(ret, staticField) {
 				ret = append(ret, staticField)
 			}
 		}
@@ -519,7 +521,7 @@ func detectSubNode(node parser.Node, parserCTX parser.UnixParserCtx) []string {
 		if subnode.Grok.RunTimeRegexp != nil {
 			for _, capturedField := range subnode.Grok.RunTimeRegexp.Names() {
 				fieldName := fmt.Sprintf("evt.Parsed.%s", capturedField)
-				if !inSlice(fieldName, ret) {
+				if !slices.Contains(ret, fieldName) {
 					ret = append(ret, fieldName)
 				}
 			}
@@ -531,7 +533,7 @@ func detectSubNode(node parser.Node, parserCTX parser.UnixParserCtx) []string {
 			}
 			for _, capturedField := range grokCompiled.Names() {
 				fieldName := fmt.Sprintf("evt.Parsed.%s", capturedField)
-				if !inSlice(fieldName, ret) {
+				if !slices.Contains(ret, fieldName) {
 					ret = append(ret, fieldName)
 				}
 			}
@@ -540,7 +542,7 @@ func detectSubNode(node parser.Node, parserCTX parser.UnixParserCtx) []string {
 		if len(subnode.Grok.Statics) > 0 {
 			staticsField := detectStaticField(subnode.Grok.Statics)
 			for _, staticField := range staticsField {
-				if !inSlice(staticField, ret) {
+				if !slices.Contains(ret, staticField) {
 					ret = append(ret, staticField)
 				}
 			}
@@ -549,7 +551,7 @@ func detectSubNode(node parser.Node, parserCTX parser.UnixParserCtx) []string {
 		if len(subnode.Statics) > 0 {
 			staticsField := detectStaticField(subnode.Statics)
 			for _, staticField := range staticsField {
-				if !inSlice(staticField, ret) {
+				if !slices.Contains(ret, staticField) {
 					ret = append(ret, staticField)
 				}
 			}

+ 4 - 3
cmd/crowdsec-cli/machines.go

@@ -18,6 +18,7 @@ import (
 	"github.com/google/uuid"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
+	"golang.org/x/exp/slices"
 	"gopkg.in/yaml.v2"
 
 	"github.com/crowdsecurity/machineid"
@@ -33,8 +34,8 @@ var (
 )
 
 func generatePassword(length int) string {
-	upper  := "ABCDEFGHIJKLMNOPQRSTUVWXY"
-	lower  := "abcdefghijklmnopqrstuvwxyz"
+	upper := "ABCDEFGHIJKLMNOPQRSTUVWXY"
+	lower := "abcdefghijklmnopqrstuvwxyz"
 	digits := "0123456789"
 
 	charset := upper + lower + digits
@@ -344,7 +345,7 @@ func NewMachinesDeleteCmd() *cobra.Command {
 			}
 			ret := make([]string, 0)
 			for _, machine := range machines {
-				if strings.Contains(machine.MachineId, toComplete) && !inSlice(machine.MachineId, args) {
+				if strings.Contains(machine.MachineId, toComplete) && !slices.Contains(args, machine.MachineId) {
 					ret = append(ret, machine.MachineId)
 				}
 			}

+ 5 - 2
cmd/crowdsec-cli/main.go

@@ -12,6 +12,7 @@ import (
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
 	"github.com/spf13/cobra/doc"
+	"golang.org/x/exp/slices"
 
 	"github.com/crowdsecurity/crowdsec/pkg/csconfig"
 	"github.com/crowdsecurity/crowdsec/pkg/cwhub"
@@ -36,6 +37,8 @@ var all bool
 
 var prometheusURL string
 
+var mergedConfig string
+
 func initConfig() {
 	var err error
 	if trace_lvl {
@@ -50,8 +53,8 @@ func initConfig() {
 		log.SetLevel(log.ErrorLevel)
 	}
 
-	if !inSlice(os.Args[1], NoNeedConfig) {
-		csConfig, err = csconfig.NewConfig(ConfigFilePath, false, false, true)
+	if !slices.Contains(NoNeedConfig, os.Args[1]) {
+		csConfig, mergedConfig, err = csconfig.NewConfig(ConfigFilePath, false, false, true)
 		if err != nil {
 			log.Fatal(err)
 		}

+ 43 - 28
cmd/crowdsec-cli/metrics.go

@@ -5,7 +5,6 @@ import (
 	"fmt"
 	"io"
 	"net/http"
-	"os"
 	"strconv"
 	"strings"
 	"time"
@@ -17,12 +16,13 @@ import (
 	"github.com/spf13/cobra"
 	"gopkg.in/yaml.v2"
 
-	"github.com/crowdsecurity/crowdsec/pkg/types"
+	"github.com/crowdsecurity/go-cs-lib/pkg/trace"
 )
 
 // FormatPrometheusMetrics is a complete rip from prom2json
 func FormatPrometheusMetrics(out io.Writer, url string, formatType string) error {
 	mfChan := make(chan *dto.MetricFamily, 1024)
+	errChan := make(chan error, 1)
 
 	// Start with the DefaultTransport for sane defaults.
 	transport := http.DefaultTransport.(*http.Transport).Clone()
@@ -32,17 +32,24 @@ func FormatPrometheusMetrics(out io.Writer, url string, formatType string) error
 	// Timeout early if the server doesn't even return the headers.
 	transport.ResponseHeaderTimeout = time.Minute
 	go func() {
-		defer types.CatchPanic("crowdsec/ShowPrometheus")
+		defer trace.CatchPanic("crowdsec/ShowPrometheus")
 		err := prom2json.FetchMetricFamilies(url, mfChan, transport)
 		if err != nil {
-			log.Fatalf("failed to fetch prometheus metrics : %v", err)
+			errChan <- fmt.Errorf("failed to fetch prometheus metrics: %w", err)
+			return
 		}
+		errChan <- nil
 	}()
 
 	result := []*prom2json.Family{}
 	for mf := range mfChan {
 		result = append(result, prom2json.NewFamily(mf))
 	}
+
+	if err := <-errChan; err != nil {
+		return err
+	}
+
 	log.Debugf("Finished reading prometheus output, %d entries", len(result))
 	/*walk*/
 	lapi_decisions_stats := map[string]struct {
@@ -262,36 +269,44 @@ func FormatPrometheusMetrics(out io.Writer, url string, formatType string) error
 
 var noUnit bool
 
+
+func runMetrics(cmd *cobra.Command, args []string) error {
+	if err := csConfig.LoadPrometheus(); err != nil {
+		return fmt.Errorf("failed to load prometheus config: %w", err)
+	}
+
+	if csConfig.Prometheus == nil {
+		return fmt.Errorf("prometheus section missing, can't show metrics")
+	}
+
+	if !csConfig.Prometheus.Enabled {
+		return fmt.Errorf("prometheus is not enabled, can't show metrics")
+	}
+
+	if prometheusURL == "" {
+		prometheusURL = csConfig.Cscli.PrometheusUrl
+	}
+
+	if prometheusURL == "" {
+		return fmt.Errorf("no prometheus url, please specify in %s or via -u", *csConfig.FilePath)
+	}
+
+	err := FormatPrometheusMetrics(color.Output, prometheusURL+"/metrics", csConfig.Cscli.Output)
+	if err != nil {
+		return fmt.Errorf("could not fetch prometheus metrics: %w", err)
+	}
+	return nil
+}
+
+
 func NewMetricsCmd() *cobra.Command {
-	var cmdMetrics = &cobra.Command{
+	cmdMetrics := &cobra.Command{
 		Use:               "metrics",
 		Short:             "Display crowdsec prometheus metrics.",
 		Long:              `Fetch metrics from the prometheus server and display them in a human-friendly way`,
 		Args:              cobra.ExactArgs(0),
 		DisableAutoGenTag: true,
-		Run: func(cmd *cobra.Command, args []string) {
-			if err := csConfig.LoadPrometheus(); err != nil {
-				log.Fatal(err)
-			}
-			if !csConfig.Prometheus.Enabled {
-				log.Warning("Prometheus is not enabled, can't show metrics")
-				os.Exit(1)
-			}
-
-			if prometheusURL == "" {
-				prometheusURL = csConfig.Cscli.PrometheusUrl
-			}
-
-			if prometheusURL == "" {
-				log.Errorf("No prometheus url, please specify in %s or via -u", *csConfig.FilePath)
-				os.Exit(1)
-			}
-
-			err := FormatPrometheusMetrics(color.Output, prometheusURL+"/metrics", csConfig.Cscli.Output)
-			if err != nil {
-				log.Fatalf("could not fetch prometheus metrics: %s", err)
-			}
-		},
+		RunE: runMetrics,
 	}
 	cmdMetrics.PersistentFlags().StringVarP(&prometheusURL, "url", "u", "", "Prometheus url (http://<ip>:<port>/metrics)")
 	cmdMetrics.PersistentFlags().BoolVar(&noUnit, "no-unit", false, "Show the real number instead of formatted with units")

+ 3 - 2
cmd/crowdsec-cli/notifications.go

@@ -20,11 +20,12 @@ import (
 	"github.com/spf13/cobra"
 	"gopkg.in/tomb.v2"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/version"
+
 	"github.com/crowdsecurity/crowdsec/pkg/apiclient"
 	"github.com/crowdsecurity/crowdsec/pkg/csconfig"
 	"github.com/crowdsecurity/crowdsec/pkg/csplugin"
 	"github.com/crowdsecurity/crowdsec/pkg/csprofiles"
-	"github.com/crowdsecurity/crowdsec/pkg/cwversion"
 )
 
 
@@ -273,7 +274,7 @@ cscli notifications reinject <alert_id> -a '{"remediation": true,"scenario":"not
 			client, err := apiclient.NewClient(&apiclient.Config{
 				MachineID:     csConfig.API.Client.Credentials.Login,
 				Password:      strfmt.Password(csConfig.API.Client.Credentials.Password),
-				UserAgent:     fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+				UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
 				URL:           apiURL,
 				VersionPrefix: "v1",
 			})

+ 8 - 6
cmd/crowdsec-cli/papi.go

@@ -1,15 +1,17 @@
 package main
 
 import (
+	"fmt"
 	"time"
 
-	"github.com/crowdsecurity/crowdsec/pkg/apiserver"
-	"github.com/crowdsecurity/crowdsec/pkg/database"
-	"github.com/crowdsecurity/crowdsec/pkg/types"
-	"github.com/pkg/errors"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
 	"gopkg.in/tomb.v2"
+
+	"github.com/crowdsecurity/go-cs-lib/pkg/ptr"
+
+	"github.com/crowdsecurity/crowdsec/pkg/apiserver"
+	"github.com/crowdsecurity/crowdsec/pkg/database"
 )
 
 func NewPapiCmd() *cobra.Command {
@@ -20,7 +22,7 @@ func NewPapiCmd() *cobra.Command {
 		DisableAutoGenTag: true,
 		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
 			if err := csConfig.LoadAPIServer(); err != nil || csConfig.DisableAPI {
-				return errors.Wrap(err, "Local API is disabled, please run this command on the local API machine")
+				return fmt.Errorf("Local API is disabled, please run this command on the local API machine: %w", err)
 			}
 			if csConfig.API.Server.OnlineClient == nil {
 				log.Fatalf("no configuration for Central API in '%s'", *csConfig.FilePath)
@@ -71,7 +73,7 @@ func NewPapiStatusCmd() *cobra.Command {
 			var lastTimestampStr *string
 			lastTimestampStr, err = dbClient.GetConfigItem(apiserver.PapiPullKey)
 			if err != nil {
-				lastTimestampStr = types.StrPtr("never")
+				lastTimestampStr = ptr.Of("never")
 			}
 			log.Infof("You can successfully interact with Polling API (PAPI)")
 			log.Infof("Console plan: %s", perms.Plan)

+ 3 - 2
cmd/crowdsec-cli/simulation.go

@@ -6,6 +6,7 @@ import (
 
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
+	"golang.org/x/exp/slices"
 	"gopkg.in/yaml.v2"
 
 	"github.com/crowdsecurity/crowdsec/pkg/cwhub"
@@ -161,7 +162,7 @@ func NewSimulationEnableCmd() *cobra.Command {
 					if !item.Installed {
 						log.Warningf("'%s' isn't enabled", scenario)
 					}
-					isExcluded := inSlice(scenario, csConfig.Cscli.SimulationConfig.Exclusions)
+					isExcluded := slices.Contains(csConfig.Cscli.SimulationConfig.Exclusions, scenario)
 					if *csConfig.Cscli.SimulationConfig.Simulation && !isExcluded {
 						log.Warning("global simulation is already enabled")
 						continue
@@ -210,7 +211,7 @@ func NewSimulationDisableCmd() *cobra.Command {
 		Run: func(cmd *cobra.Command, args []string) {
 			if len(args) > 0 {
 				for _, scenario := range args {
-					isExcluded := inSlice(scenario, csConfig.Cscli.SimulationConfig.Exclusions)
+					isExcluded := slices.Contains(csConfig.Cscli.SimulationConfig.Exclusions, scenario)
 					if !*csConfig.Cscli.SimulationConfig.Simulation && !isExcluded {
 						log.Warningf("%s isn't in simulation mode", scenario)
 						continue

+ 3 - 1
cmd/crowdsec-cli/support.go

@@ -18,6 +18,8 @@ import (
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/version"
+
 	"github.com/crowdsecurity/crowdsec/pkg/apiclient"
 	"github.com/crowdsecurity/crowdsec/pkg/cwhub"
 	"github.com/crowdsecurity/crowdsec/pkg/cwversion"
@@ -182,7 +184,7 @@ func collectAPIStatus(login string, password string, endpoint string, prefix str
 
 	Client, err = apiclient.NewDefaultClient(apiurl,
 		prefix,
-		fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+		fmt.Sprintf("crowdsec/%s", version.String()),
 		nil)
 	if err != nil {
 		return []byte(fmt.Sprintf("could not init client: %s", err))

+ 12 - 36
cmd/crowdsec-cli/utils.go

@@ -19,8 +19,11 @@ import (
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
 	"github.com/texttheater/golang-levenshtein/levenshtein"
+	"golang.org/x/exp/slices"
 	"gopkg.in/yaml.v2"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/trace"
+
 	"github.com/crowdsecurity/crowdsec/pkg/cwhub"
 	"github.com/crowdsecurity/crowdsec/pkg/database"
 	"github.com/crowdsecurity/crowdsec/pkg/types"
@@ -35,15 +38,6 @@ func printHelp(cmd *cobra.Command) {
 	}
 }
 
-func inSlice(s string, slice []string) bool {
-	for _, str := range slice {
-		if s == str {
-			return true
-		}
-	}
-	return false
-}
-
 func indexOf(s string, slice []string) int {
 	for i, elem := range slice {
 		if s == elem {
@@ -113,7 +107,7 @@ func compAllItems(itemType string, args []string, toComplete string) ([]string,
 	comp := make([]string, 0)
 	hubItems := cwhub.GetHubStatusForItemType(itemType, "", true)
 	for _, item := range hubItems {
-		if !inSlice(item.Name, args) && strings.Contains(item.Name, toComplete) {
+		if !slices.Contains(args, item.Name) && strings.Contains(item.Name, toComplete) {
 			comp = append(comp, item.Name)
 		}
 	}
@@ -515,7 +509,7 @@ func GetPrometheusMetric(url string) []*prom2json.Family {
 	transport.ResponseHeaderTimeout = time.Minute
 
 	go func() {
-		defer types.CatchPanic("crowdsec/GetPrometheusMetric")
+		defer trace.CatchPanic("crowdsec/GetPrometheusMetric")
 		err := prom2json.FetchMetricFamilies(url, mfChan, transport)
 		if err != nil {
 			log.Fatalf("failed to fetch prometheus metrics : %v", err)
@@ -691,30 +685,13 @@ type unit struct {
 }
 
 var ranges = []unit{
-	{
-		value:  1e18,
-		symbol: "E",
-	},
-	{
-		value:  1e15,
-		symbol: "P",
-	},
-	{
-		value:  1e12,
-		symbol: "T",
-	},
-	{
-		value:  1e6,
-		symbol: "M",
-	},
-	{
-		value:  1e3,
-		symbol: "k",
-	},
-	{
-		value:  1,
-		symbol: "",
-	},
+	{value: 1e18, symbol: "E"},
+	{value: 1e15, symbol: "P"},
+	{value: 1e12, symbol: "T"},
+	{value: 1e9,  symbol: "G"},
+	{value: 1e6,  symbol: "M"},
+	{value: 1e3,  symbol: "k"},
+	{value: 1,    symbol: ""},
 }
 
 func formatNumber(num int) string {
@@ -746,7 +723,6 @@ func getDBClient() (*database.Client, error) {
 	return ret, nil
 }
 
-
 func removeFromSlice(val string, slice []string) []string {
 	var i int
 	var value string

+ 2 - 4
cmd/crowdsec/Makefile

@@ -7,9 +7,7 @@ endif
 # Go parameters
 GOCMD = go
 GOBUILD = $(GOCMD) build
-GOCLEAN = $(GOCMD) clean
 GOTEST = $(GOCMD) test
-GOGET = $(GOCMD) get
 
 CROWDSEC_BIN = crowdsec$(EXT)
 # names longer than 15 chars break 'pgrep'
@@ -31,7 +29,7 @@ test:
 	$(GOTEST) $(LD_OPTS) -v ./...
 
 clean:
-	@$(RM) $(CROWDSEC_BIN) $(CROWDSEC_BIN).test $(WIN_IGNORE_ERR)
+	@$(RM) $(CROWDSEC_BIN) $(WIN_IGNORE_ERR)
 
 .PHONY: install
 install: install-conf install-bin
@@ -59,7 +57,7 @@ install-conf:
 install-bin:
 	install -v -m 755 -D "$(CROWDSEC_BIN)" "$(BIN_PREFIX)/$(CROWDSEC_BIN)" || exit
 
-.PHONY: systemd"$(BIN_PREFI"$(BIN_PREFIX)/$(CROWDSEC_BIN)""$(BIN_PREFIX)/$(CROWDSEC_BIN)"X)/$(CROWDSEC_BIN)"
+.PHONY: systemd
 systemd: install
 	CFG=$(CFG_PREFIX) PID=$(PID_DIR) BIN=$(BIN_PREFIX)"/"$(CROWDSEC_BIN) envsubst < ../../config/crowdsec.service > "$(SYSTEMD_PATH_FILE)"
 	systemctl daemon-reload

+ 4 - 3
cmd/crowdsec/api.go

@@ -7,9 +7,10 @@ import (
 	"github.com/pkg/errors"
 	log "github.com/sirupsen/logrus"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/trace"
+
 	"github.com/crowdsecurity/crowdsec/pkg/apiserver"
 	"github.com/crowdsecurity/crowdsec/pkg/csconfig"
-	"github.com/crowdsecurity/crowdsec/pkg/types"
 )
 
 func initAPIServer(cConfig *csconfig.Config) (*apiserver.APIServer, error) {
@@ -52,9 +53,9 @@ func initAPIServer(cConfig *csconfig.Config) (*apiserver.APIServer, error) {
 
 func serveAPIServer(apiServer *apiserver.APIServer, apiReady chan bool) {
 	apiTomb.Go(func() error {
-		defer types.CatchPanic("crowdsec/serveAPIServer")
+		defer trace.CatchPanic("crowdsec/serveAPIServer")
 		go func() {
-			defer types.CatchPanic("crowdsec/runAPIServer")
+			defer trace.CatchPanic("crowdsec/runAPIServer")
 			log.Debugf("serving API after %s ms", time.Since(crowdsecT0))
 			if err := apiServer.Run(apiReady); err != nil {
 				log.Fatal(err)

+ 7 - 5
cmd/crowdsec/crowdsec.go

@@ -8,6 +8,8 @@ import (
 
 	"path/filepath"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/trace"
+
 	"github.com/crowdsecurity/crowdsec/pkg/acquisition"
 	"github.com/crowdsecurity/crowdsec/pkg/csconfig"
 	"github.com/crowdsecurity/crowdsec/pkg/cwhub"
@@ -53,7 +55,7 @@ func runCrowdsec(cConfig *csconfig.Config, parsers *parser.Parsers) error {
 		parserWg.Add(1)
 		for i := 0; i < cConfig.Crowdsec.ParserRoutinesCount; i++ {
 			parsersTomb.Go(func() error {
-				defer types.CatchPanic("crowdsec/runParse")
+				defer trace.CatchPanic("crowdsec/runParse")
 				if err := runParse(inputLineChan, inputEventChan, *parsers.Ctx, parsers.Nodes); err != nil { //this error will never happen as parser.Parse is not able to return errors
 					log.Fatalf("starting parse error : %s", err)
 					return err
@@ -79,7 +81,7 @@ func runCrowdsec(cConfig *csconfig.Config, parsers *parser.Parsers) error {
 
 		for i := 0; i < cConfig.Crowdsec.BucketsRoutinesCount; i++ {
 			bucketsTomb.Go(func() error {
-				defer types.CatchPanic("crowdsec/runPour")
+				defer trace.CatchPanic("crowdsec/runPour")
 				if err := runPour(inputEventChan, holders, buckets, cConfig); err != nil {
 					log.Fatalf("starting pour error : %s", err)
 					return err
@@ -97,7 +99,7 @@ func runCrowdsec(cConfig *csconfig.Config, parsers *parser.Parsers) error {
 		outputWg.Add(1)
 		for i := 0; i < cConfig.Crowdsec.OutputRoutinesCount; i++ {
 			outputsTomb.Go(func() error {
-				defer types.CatchPanic("crowdsec/runOutput")
+				defer trace.CatchPanic("crowdsec/runOutput")
 				if err := runOutput(inputEventChan, outputEventChan, buckets, *parsers.Povfwctx, parsers.Povfwnodes, *cConfig.API.Client.Credentials); err != nil {
 					log.Fatalf("starting outputs error : %s", err)
 					return err
@@ -132,9 +134,9 @@ func runCrowdsec(cConfig *csconfig.Config, parsers *parser.Parsers) error {
 
 func serveCrowdsec(parsers *parser.Parsers, cConfig *csconfig.Config, agentReady chan bool) {
 	crowdsecTomb.Go(func() error {
-		defer types.CatchPanic("crowdsec/serveCrowdsec")
+		defer trace.CatchPanic("crowdsec/serveCrowdsec")
 		go func() {
-			defer types.CatchPanic("crowdsec/runCrowdsec")
+			defer trace.CatchPanic("crowdsec/runCrowdsec")
 			// this logs every time, even at config reload
 			log.Debugf("running agent after %s ms", time.Since(crowdsecT0))
 			agentReady <- true

+ 4 - 2
cmd/crowdsec/main.go

@@ -154,7 +154,9 @@ func (f *Flags) Parse() {
 	flag.BoolVar(&f.DisableAgent, "no-cs", false, "disable crowdsec agent")
 	flag.BoolVar(&f.DisableAPI, "no-api", false, "disable local API")
 	flag.BoolVar(&f.DisableCAPI, "no-capi", false, "disable communication with Central API")
-	flag.StringVar(&f.WinSvc, "winsvc", "", "Windows service Action : Install, Remove etc..")
+	if runtime.GOOS == "windows" {
+		flag.StringVar(&f.WinSvc, "winsvc", "", "Windows service Action: Install, Remove etc..")
+	}
 	flag.StringVar(&dumpFolder, "dump-data", "", "dump parsers/buckets raw outputs")
 	flag.Parse()
 }
@@ -192,7 +194,7 @@ func newLogLevel(curLevelPtr *log.Level, f *Flags) *log.Level {
 
 // LoadConfig returns a configuration parsed from configuration file
 func LoadConfig(configFile string, disableAgent bool, disableAPI bool, quiet bool) (*csconfig.Config, error) {
-	cConfig, err := csconfig.NewConfig(configFile, disableAgent, disableAPI, quiet)
+	cConfig, _, err := csconfig.NewConfig(configFile, disableAgent, disableAPI, quiet)
 	if err != nil {
 		return nil, err
 	}

+ 5 - 4
cmd/crowdsec/metrics.go

@@ -9,15 +9,16 @@ import (
 	"github.com/prometheus/client_golang/prometheus/promhttp"
 	log "github.com/sirupsen/logrus"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/trace"
+	"github.com/crowdsecurity/go-cs-lib/pkg/version"
+
 	v1 "github.com/crowdsecurity/crowdsec/pkg/apiserver/controllers/v1"
 	"github.com/crowdsecurity/crowdsec/pkg/cache"
 	"github.com/crowdsecurity/crowdsec/pkg/csconfig"
-	"github.com/crowdsecurity/crowdsec/pkg/cwversion"
 	"github.com/crowdsecurity/crowdsec/pkg/database"
 	"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
 	leaky "github.com/crowdsecurity/crowdsec/pkg/leakybucket"
 	"github.com/crowdsecurity/crowdsec/pkg/parser"
-	"github.com/crowdsecurity/crowdsec/pkg/types"
 )
 
 /*prometheus*/
@@ -61,7 +62,7 @@ var globalCsInfo = prometheus.NewGauge(
 	prometheus.GaugeOpts{
 		Name:        "cs_info",
 		Help:        "Information about Crowdsec.",
-		ConstLabels: prometheus.Labels{"version": cwversion.VersionStr()},
+		ConstLabels: prometheus.Labels{"version": version.String()},
 	},
 )
 
@@ -187,7 +188,7 @@ func servePrometheus(config *csconfig.PrometheusCfg, dbClient *database.Client,
 		return
 	}
 
-	defer types.CatchPanic("crowdsec/servePrometheus")
+	defer trace.CatchPanic("crowdsec/servePrometheus")
 
 	http.Handle("/metrics", computeDynamicMetrics(promhttp.Handler(), dbClient))
 	<-apiReady

+ 3 - 2
cmd/crowdsec/output.go

@@ -7,10 +7,11 @@ import (
 	"sync"
 	"time"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/version"
+
 	"github.com/crowdsecurity/crowdsec/pkg/apiclient"
 	"github.com/crowdsecurity/crowdsec/pkg/csconfig"
 	"github.com/crowdsecurity/crowdsec/pkg/cwhub"
-	"github.com/crowdsecurity/crowdsec/pkg/cwversion"
 	leaky "github.com/crowdsecurity/crowdsec/pkg/leakybucket"
 	"github.com/crowdsecurity/crowdsec/pkg/models"
 	"github.com/crowdsecurity/crowdsec/pkg/parser"
@@ -88,7 +89,7 @@ func runOutput(input chan types.Event, overflow chan types.Event, buckets *leaky
 		MachineID:      apiConfig.Login,
 		Password:       password,
 		Scenarios:      scenarios,
-		UserAgent:      fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+		UserAgent:      fmt.Sprintf("crowdsec/%s", version.String()),
 		URL:            apiURL,
 		PapiURL:        papiURL,
 		VersionPrefix:  "v1",

+ 5 - 4
cmd/crowdsec/run_in_svc.go

@@ -10,10 +10,11 @@ import (
 	log "github.com/sirupsen/logrus"
 	"github.com/sirupsen/logrus/hooks/writer"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/trace"
+	"github.com/crowdsecurity/go-cs-lib/pkg/version"
+
 	"github.com/crowdsecurity/crowdsec/pkg/csconfig"
-	"github.com/crowdsecurity/crowdsec/pkg/cwversion"
 	"github.com/crowdsecurity/crowdsec/pkg/database"
-	"github.com/crowdsecurity/crowdsec/pkg/types"
 )
 
 func StartRunSvc() error {
@@ -22,7 +23,7 @@ func StartRunSvc() error {
 		err     error
 	)
 
-	defer types.CatchPanic("crowdsec/StartRunSvc")
+	defer trace.CatchPanic("crowdsec/StartRunSvc")
 
 	// Set a default logger with level=fatal on stderr,
 	// in addition to the one we configure afterwards
@@ -38,7 +39,7 @@ func StartRunSvc() error {
 		return err
 	}
 
-	log.Infof("Crowdsec %s", cwversion.VersionStr())
+	log.Infof("Crowdsec %s", version.String())
 
 	apiReady := make(chan bool, 1)
 	agentReady := make(chan bool, 1)

+ 5 - 4
cmd/crowdsec/run_in_svc_windows.go

@@ -7,17 +7,18 @@ import (
 	log "github.com/sirupsen/logrus"
 	"golang.org/x/sys/windows/svc"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/trace"
+	"github.com/crowdsecurity/go-cs-lib/pkg/version"
+
 	"github.com/crowdsecurity/crowdsec/pkg/csconfig"
-	"github.com/crowdsecurity/crowdsec/pkg/cwversion"
 	"github.com/crowdsecurity/crowdsec/pkg/database"
-	"github.com/crowdsecurity/crowdsec/pkg/types"
 )
 
 func StartRunSvc() error {
 	const svcName = "CrowdSec"
 	const svcDescription = "Crowdsec IPS/IDS"
 
-	defer types.CatchPanic("crowdsec/StartRunSvc")
+	defer trace.CatchPanic("crowdsec/StartRunSvc")
 
 	isRunninginService, err := svc.IsWindowsService()
 	if err != nil {
@@ -66,7 +67,7 @@ func WindowsRun() error {
 		return err
 	}
 	// Configure logging
-	log.Infof("Crowdsec %s", cwversion.VersionStr())
+	log.Infof("Crowdsec %s", version.String())
 
 	apiReady := make(chan bool, 1)
 	agentReady := make(chan bool, 1)

+ 3 - 1
cmd/crowdsec/serve.go

@@ -11,6 +11,8 @@ import (
 	log "github.com/sirupsen/logrus"
 	"gopkg.in/tomb.v2"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/trace"
+
 	"github.com/crowdsecurity/crowdsec/pkg/csconfig"
 	"github.com/crowdsecurity/crowdsec/pkg/database"
 	"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
@@ -226,7 +228,7 @@ func HandleSignals(cConfig *csconfig.Config) error {
 	exitChan := make(chan error)
 
 	go func() {
-		defer types.CatchPanic("crowdsec/HandleSignals")
+		defer trace.CatchPanic("crowdsec/HandleSignals")
 	Loop:
 		for {
 			s := <-signalChan

+ 24 - 1
config/acquis_win.yaml

@@ -1,3 +1,4 @@
+##RDP
 source: wineventlog
 event_channel: Security
 event_ids:
@@ -5,4 +6,26 @@ event_ids:
  - 4623
 event_level: information
 labels:
- type: eventlog
+ type: eventlog
+---
+##Firewall
+filenames:
+  - C:\Windows\System32\LogFiles\Firewall\pfirewall.log
+labels:
+  type: windows-firewall
+---
+##SQL Server
+source: wineventlog
+event_channel: Application
+event_ids:
+ - 18456
+event_level: information
+labels:
+ type: eventlog
+---
+##IIS
+use_time_machine: true
+filenames:
+  - C:\inetpub\logs\LogFiles\*\*.log
+labels:
+  type: iis

+ 1 - 2
config/simulation.yaml

@@ -1,4 +1,3 @@
-simulation: off
+simulation: false
 # exclusions:
 #  - crowdsecurity/ssh-bf
- 

+ 0 - 6
debian/rules

@@ -4,12 +4,6 @@ export DEB_VERSION=$(shell dpkg-parsechangelog | egrep '^Version:' | cut -f 2 -d
 export BUILD_VERSION=v${DEB_VERSION}-debian-pragmatic
 export GO111MODULE=on
 
-# LD_OPTS=-ldflags "-s -w -X github.com/crowdsecurity/crowdsec/pkg/cwversion.Version=$(BUILD_VERSION) \
-# -X github.com/crowdsecurity/crowdsec/pkg/cwversion.BuildDate=$(BUILD_TIMESTAMP) \
-# -X github.com/crowdsecurity/crowdsec/pkg/cwversion.Codename=$(BUILD_CODENAME)  \
-# -X github.com/crowdsecurity/crowdsec/pkg/cwversion.Tag=$(BUILD_TAG) \
-# -X github.com/crowdsecurity/crowdsec/pkg/cwversion.GoVersion=$(BUILD_GOVERSION)"
-
 %:
 	dh $@
 

+ 1 - 0
docker/README.md

@@ -280,6 +280,7 @@ config.yaml) each time the container is run.
 | __LAPI__                | | (useless with DISABLE_LOCAL_API) |
 | `USE_WAL`               | false | Enable Write-Ahead Logging with SQLite |
 | `CUSTOM_HOSTNAME`       | localhost | Name for the local agent (running in the container with LAPI) |
+| `CAPI_WHITELISTS_PATH`  | | Path for capi_whitelists.yaml |
 |                         | | |
 | __Agent__               | | (these don't work with DISABLE_AGENT) |
 | `TYPE`                  | | [`Labels.type`](https://docs.crowdsec.net/Crowdsec/v1/references/acquisition/) for file in time-machine: `-e TYPE="<type>"` |

+ 5 - 3
docker/docker_start.sh

@@ -187,7 +187,6 @@ fi
 
 lapi_credentials_path=$(conf_get '.api.client.credentials_path')
 
-
 if isfalse "$DISABLE_LOCAL_API"; then
     # generate local agent credentials (even if agent is disabled, cscli needs a
     # connection to the API)
@@ -365,6 +364,11 @@ for BOUNCER in /run/secrets/@(bouncer_key|BOUNCER_KEY)* ; do
 done
 shopt -u nullglob extglob
 
+# set all options before validating the configuration
+
+conf_set_if "$CAPI_WHITELISTS_PATH" '.api.server.capi_whitelists_path = strenv(CAPI_WHITELISTS_PATH)'
+conf_set_if "$METRICS_PORT" '.prometheus.listen_port=env(METRICS_PORT)'
+
 ARGS=""
 if [ "$CONFIG_FILE" != "" ]; then
     ARGS="-c $CONFIG_FILE"
@@ -402,7 +406,5 @@ if istrue "$LEVEL_INFO"; then
     ARGS="$ARGS -info"
 fi
 
-conf_set_if "$METRICS_PORT" '.prometheus.listen_port=env(METRICS_PORT)'
-
 # shellcheck disable=SC2086
 exec crowdsec $ARGS

+ 2 - 2
docker/test/Pipfile

@@ -1,11 +1,11 @@
 [packages]
 pytest-dotenv = "*"
 pytest-xdist = "*"
-pytest-cs = {ref = "0.4.0", git = "https://github.com/crowdsecurity/pytest-cs.git"}
+pytest-cs = {ref = "0.7.16", git = "https://github.com/crowdsecurity/pytest-cs.git"}
 
 [dev-packages]
 gnureadline = "*"
 ipdb = "*"
 
 [requires]
-python_version = "3.10"
+python_version = "*"

+ 77 - 112
docker/test/Pipfile.lock

@@ -1,11 +1,11 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "da2959f993eb751a5f6d2b1c4537ba39ed414d0e9d300dc513ced5a8f0ab4261"
+            "sha256": "7e91f125d4ad0d1f1b5da7ef441d75baf4f28788c791803a216cb6956b131ea9"
         },
         "pipfile-spec": 6,
         "requires": {
-            "python_version": "3.10"
+            "python_version": "*"
         },
         "sources": [
             {
@@ -16,21 +16,13 @@
         ]
     },
     "default": {
-        "attrs": {
-            "hashes": [
-                "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836",
-                "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"
-            ],
-            "markers": "python_version >= '3.6'",
-            "version": "==22.2.0"
-        },
         "certifi": {
             "hashes": [
-                "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3",
-                "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"
+                "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7",
+                "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"
             ],
             "markers": "python_version >= '3.6'",
-            "version": "==2022.12.7"
+            "version": "==2023.5.7"
         },
         "cffi": {
             "hashes": [
@@ -184,48 +176,36 @@
         },
         "cryptography": {
             "hashes": [
-                "sha256:103e8f7155f3ce2ffa0049fe60169878d47a4364b277906386f8de21c9234aa1",
-                "sha256:23df8ca3f24699167daf3e23e51f7ba7334d504af63a94af468f468b975b7dd7",
-                "sha256:2725672bb53bb92dc7b4150d233cd4b8c59615cd8288d495eaa86db00d4e5c06",
-                "sha256:30b1d1bfd00f6fc80d11300a29f1d8ab2b8d9febb6ed4a38a76880ec564fae84",
-                "sha256:35d658536b0a4117c885728d1a7032bdc9a5974722ae298d6c533755a6ee3915",
-                "sha256:50cadb9b2f961757e712a9737ef33d89b8190c3ea34d0fb6675e00edbe35d074",
-                "sha256:5f8c682e736513db7d04349b4f6693690170f95aac449c56f97415c6980edef5",
-                "sha256:6236a9610c912b129610eb1a274bdc1350b5df834d124fa84729ebeaf7da42c3",
-                "sha256:788b3921d763ee35dfdb04248d0e3de11e3ca8eb22e2e48fef880c42e1f3c8f9",
-                "sha256:8bc0008ef798231fac03fe7d26e82d601d15bd16f3afaad1c6113771566570f3",
-                "sha256:8f35c17bd4faed2bc7797d2a66cbb4f986242ce2e30340ab832e5d99ae60e011",
-                "sha256:b49a88ff802e1993b7f749b1eeb31134f03c8d5c956e3c125c75558955cda536",
-                "sha256:bc0521cce2c1d541634b19f3ac661d7a64f9555135e9d8af3980965be717fd4a",
-                "sha256:bc5b871e977c8ee5a1bbc42fa8d19bcc08baf0c51cbf1586b0e87a2694dde42f",
-                "sha256:c43ac224aabcbf83a947eeb8b17eaf1547bce3767ee2d70093b461f31729a480",
-                "sha256:d15809e0dbdad486f4ad0979753518f47980020b7a34e9fc56e8be4f60702fac",
-                "sha256:d7d84a512a59f4412ca8549b01f94be4161c94efc598bf09d027d67826beddc0",
-                "sha256:e029b844c21116564b8b61216befabca4b500e6816fa9f0ba49527653cae2108",
-                "sha256:e8a0772016feeb106efd28d4a328e77dc2edae84dfbac06061319fdb669ff828",
-                "sha256:e944fe07b6f229f4c1a06a7ef906a19652bdd9fd54c761b0ff87e83ae7a30354",
-                "sha256:eb40fe69cfc6f5cdab9a5ebd022131ba21453cf7b8a7fd3631f45bbf52bed612",
-                "sha256:fa507318e427169ade4e9eccef39e9011cdc19534f55ca2f36ec3f388c1f70f3",
-                "sha256:ffd394c7896ed7821a6d13b24657c6a34b6e2650bd84ae063cf11ccffa4f1a97"
+                "sha256:05dc219433b14046c476f6f09d7636b92a1c3e5808b9a6536adf4932b3b2c440",
+                "sha256:0dcca15d3a19a66e63662dc8d30f8036b07be851a8680eda92d079868f106288",
+                "sha256:142bae539ef28a1c76794cca7f49729e7c54423f615cfd9b0b1fa90ebe53244b",
+                "sha256:3daf9b114213f8ba460b829a02896789751626a2a4e7a43a28ee77c04b5e4958",
+                "sha256:48f388d0d153350f378c7f7b41497a54ff1513c816bcbbcafe5b829e59b9ce5b",
+                "sha256:4df2af28d7bedc84fe45bd49bc35d710aede676e2a4cb7fc6d103a2adc8afe4d",
+                "sha256:4f01c9863da784558165f5d4d916093737a75203a5c5286fde60e503e4276c7a",
+                "sha256:7a38250f433cd41df7fcb763caa3ee9362777fdb4dc642b9a349721d2bf47404",
+                "sha256:8f79b5ff5ad9d3218afb1e7e20ea74da5f76943ee5edb7f76e56ec5161ec782b",
+                "sha256:956ba8701b4ffe91ba59665ed170a2ebbdc6fc0e40de5f6059195d9f2b33ca0e",
+                "sha256:a04386fb7bc85fab9cd51b6308633a3c271e3d0d3eae917eebab2fac6219b6d2",
+                "sha256:a95f4802d49faa6a674242e25bfeea6fc2acd915b5e5e29ac90a32b1139cae1c",
+                "sha256:adc0d980fd2760c9e5de537c28935cc32b9353baaf28e0814df417619c6c8c3b",
+                "sha256:aecbb1592b0188e030cb01f82d12556cf72e218280f621deed7d806afd2113f9",
+                "sha256:b12794f01d4cacfbd3177b9042198f3af1c856eedd0a98f10f141385c809a14b",
+                "sha256:c0764e72b36a3dc065c155e5b22f93df465da9c39af65516fe04ed3c68c92636",
+                "sha256:c33c0d32b8594fa647d2e01dbccc303478e16fdd7cf98652d5b3ed11aa5e5c99",
+                "sha256:cbaba590180cba88cb99a5f76f90808a624f18b169b90a4abb40c1fd8c19420e",
+                "sha256:d5a1bd0e9e2031465761dfa920c16b0065ad77321d8a8c1f5ee331021fda65e9"
             ],
             "markers": "python_version >= '3.6'",
-            "version": "==39.0.2"
+            "version": "==40.0.2"
         },
         "docker": {
             "hashes": [
-                "sha256:896c4282e5c7af5c45e8b683b0b0c33932974fe6e50fc6906a0a83616ab3da97",
-                "sha256:dbcb3bd2fa80dca0788ed908218bf43972772009b881ed1e20dfc29a65e49782"
+                "sha256:134cd828f84543cbf8e594ff81ca90c38288df3c0a559794c12f2e4b634ea19e",
+                "sha256:dcc088adc2ec4e7cfc594e275d8bd2c9738c56c808de97476939ef67db5af8c2"
             ],
             "markers": "python_version >= '3.7'",
-            "version": "==6.0.1"
-        },
-        "exceptiongroup": {
-            "hashes": [
-                "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e",
-                "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"
-            ],
-            "markers": "python_version < '3.11'",
-            "version": "==1.1.1"
+            "version": "==6.1.2"
         },
         "execnet": {
             "hashes": [
@@ -253,11 +233,11 @@
         },
         "packaging": {
             "hashes": [
-                "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2",
-                "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"
+                "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61",
+                "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"
             ],
             "markers": "python_version >= '3.7'",
-            "version": "==23.0"
+            "version": "==23.1"
         },
         "pluggy": {
             "hashes": [
@@ -269,23 +249,23 @@
         },
         "psutil": {
             "hashes": [
-                "sha256:149555f59a69b33f056ba1c4eb22bb7bf24332ce631c44a319cec09f876aaeff",
-                "sha256:16653106f3b59386ffe10e0bad3bb6299e169d5327d3f187614b1cb8f24cf2e1",
-                "sha256:3d7f9739eb435d4b1338944abe23f49584bde5395f27487d2ee25ad9a8774a62",
-                "sha256:3ff89f9b835100a825b14c2808a106b6fdcc4b15483141482a12c725e7f78549",
-                "sha256:54c0d3d8e0078b7666984e11b12b88af2db11d11249a8ac8920dd5ef68a66e08",
-                "sha256:54d5b184728298f2ca8567bf83c422b706200bcbbfafdc06718264f9393cfeb7",
-                "sha256:6001c809253a29599bc0dfd5179d9f8a5779f9dffea1da0f13c53ee568115e1e",
-                "sha256:68908971daf802203f3d37e78d3f8831b6d1014864d7a85937941bb35f09aefe",
-                "sha256:6b92c532979bafc2df23ddc785ed116fced1f492ad90a6830cf24f4d1ea27d24",
-                "sha256:852dd5d9f8a47169fe62fd4a971aa07859476c2ba22c2254d4a1baa4e10b95ad",
-                "sha256:9120cd39dca5c5e1c54b59a41d205023d436799b1c8c4d3ff71af18535728e94",
-                "sha256:c1ca331af862803a42677c120aff8a814a804e09832f166f226bfd22b56feee8",
-                "sha256:efeae04f9516907be44904cc7ce08defb6b665128992a56957abc9b61dca94b7",
-                "sha256:fd8522436a6ada7b4aad6638662966de0d61d241cb821239b2ae7013d41a43d4"
+                "sha256:104a5cc0e31baa2bcf67900be36acde157756b9c44017b86b2c049f11957887d",
+                "sha256:3c6f686f4225553615612f6d9bc21f1c0e305f75d7d8454f9b46e901778e7217",
+                "sha256:4aef137f3345082a3d3232187aeb4ac4ef959ba3d7c10c33dd73763fbc063da4",
+                "sha256:5410638e4df39c54d957fc51ce03048acd8e6d60abc0f5107af51e5fb566eb3c",
+                "sha256:5b9b8cb93f507e8dbaf22af6a2fd0ccbe8244bf30b1baad6b3954e935157ae3f",
+                "sha256:7a7dd9997128a0d928ed4fb2c2d57e5102bb6089027939f3b722f3a210f9a8da",
+                "sha256:89518112647f1276b03ca97b65cc7f64ca587b1eb0278383017c2a0dcc26cbe4",
+                "sha256:8c5f7c5a052d1d567db4ddd231a9d27a74e8e4a9c3f44b1032762bd7b9fdcd42",
+                "sha256:ab8ed1a1d77c95453db1ae00a3f9c50227ebd955437bcf2a574ba8adbf6a74d5",
+                "sha256:acf2aef9391710afded549ff602b5887d7a2349831ae4c26be7c807c0a39fac4",
+                "sha256:b258c0c1c9d145a1d5ceffab1134441c4c5113b2417fafff7315a917a026c3c9",
+                "sha256:be8929ce4313f9f8146caad4272f6abb8bf99fc6cf59344a3167ecd74f4f203f",
+                "sha256:c607bb3b57dc779d55e1554846352b4e358c10fff3abf3514a7a6601beebdb30",
+                "sha256:ea8518d152174e1249c4f2a1c89e3e6065941df2fa13a1ab45327716a23c2b48"
             ],
             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
-            "version": "==5.9.4"
+            "version": "==5.9.5"
         },
         "pycparser": {
             "hashes": [
@@ -296,15 +276,15 @@
         },
         "pytest": {
             "hashes": [
-                "sha256:130328f552dcfac0b1cec75c12e3f005619dc5f874f0a06e8ff7263f0ee6225e",
-                "sha256:c99ab0c73aceb050f68929bc93af19ab6db0558791c6a0715723abe9d0ade9d4"
+                "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362",
+                "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"
             ],
             "markers": "python_version >= '3.7'",
-            "version": "==7.2.2"
+            "version": "==7.3.1"
         },
         "pytest-cs": {
             "git": "https://github.com/crowdsecurity/pytest-cs.git",
-            "ref": "8c49bd8072672c49855a7991a5900858b3ebb777"
+            "ref": "4a3451084215053af8a48ff37507b4f86bf75c10"
         },
         "pytest-datadir": {
             "hashes": [
@@ -324,11 +304,11 @@
         },
         "pytest-xdist": {
             "hashes": [
-                "sha256:1849bd98d8b242b948e472db7478e090bf3361912a8fed87992ed94085f54727",
-                "sha256:37290d161638a20b672401deef1cba812d110ac27e35d213f091d15b8beb40c9"
+                "sha256:d5ee0520eb1b7bcca50a60a518ab7a7707992812c578198f8b44fdfac78e8c93",
+                "sha256:ff9daa7793569e6a68544850fd3927cd257cc03a7ef76c95e86915355e82b5f2"
             ],
             "index": "pypi",
-            "version": "==3.2.1"
+            "version": "==3.3.1"
         },
         "python-dotenv": {
             "hashes": [
@@ -386,42 +366,35 @@
         },
         "requests": {
             "hashes": [
-                "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa",
-                "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"
-            ],
-            "markers": "python_version >= '3.7' and python_version < '4'",
-            "version": "==2.28.2"
-        },
-        "tomli": {
-            "hashes": [
-                "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
-                "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
+                "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f",
+                "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"
             ],
-            "markers": "python_version < '3.11'",
-            "version": "==2.0.1"
+            "markers": "python_version >= '3.7'",
+            "version": "==2.31.0"
         },
         "trustme": {
             "hashes": [
-                "sha256:5e07b23d70ceed64f3bb36ae4b9abc52354c16c98d45ab037bee2b5fbffe586c",
-                "sha256:a6e53039cc43e70548ebd9a42ec1af5cba803a16d14321cd96352d2b4e010e04"
+                "sha256:1d4f0b0fe28091506edc29c19ad90cca387646add436c3ca66ba7bcc53807f55",
+                "sha256:7a9f82ad494d661cd10c9eed38e0f708154eb59a2e415da6b02af3e5dac53134"
             ],
-            "version": "==0.9.0"
+            "markers": "python_version >= '3.7'",
+            "version": "==1.0.0"
         },
         "urllib3": {
             "hashes": [
-                "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305",
-                "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"
+                "sha256:61717a1095d7e155cdb737ac7bb2f4324a858a1e2e6466f6d03ff630ca68d3cc",
+                "sha256:d055c2f9d38dc53c808f6fdc8eab7360b6fdbbde02340ed25cfbcd817c62469e"
             ],
-            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
-            "version": "==1.26.15"
+            "markers": "python_version >= '3.7'",
+            "version": "==2.0.2"
         },
         "websocket-client": {
             "hashes": [
-                "sha256:3f09e6d8230892547132177f575a4e3e73cfdf06526e20cc02aa1c3b47184d40",
-                "sha256:cdf5877568b7e83aa7cf2244ab56a3213de587bbe0ce9d8b9600fc77b455d89e"
+                "sha256:c7d67c13b928645f259d9b847ab5b57fd2d127213ca41ebd880de1f553b7c23b",
+                "sha256:f8c64e28cd700e7ba1f04350d66422b6833b82a796b525a51e740b8cc8dab4b1"
             ],
             "markers": "python_version >= '3.7'",
-            "version": "==1.5.1"
+            "version": "==1.5.2"
         }
     },
     "develop": {
@@ -444,7 +417,7 @@
                 "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330",
                 "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"
             ],
-            "markers": "python_version < '3.11' and python_version >= '3.7'",
+            "markers": "python_version >= '3.11'",
             "version": "==5.1.1"
         },
         "executing": {
@@ -497,11 +470,11 @@
         },
         "ipython": {
             "hashes": [
-                "sha256:5b54478e459155a326bf5f42ee4f29df76258c0279c36f21d71ddb560f88b156",
-                "sha256:735cede4099dbc903ee540307b9171fbfef4aa75cfcacc5a273b2cda2f02be04"
+                "sha256:7dff3fad32b97f6488e02f87b970f309d082f758d7b7fc252e3b19ee0e432dbb",
+                "sha256:ffca270240fbd21b06b2974e14a86494d6d29290184e788275f55e0b55914926"
             ],
-            "markers": "python_version < '3.11' and python_version >= '3.7'",
-            "version": "==8.11.0"
+            "markers": "python_version >= '3.11'",
+            "version": "==8.13.2"
         },
         "jedi": {
             "hashes": [
@@ -566,11 +539,11 @@
         },
         "pygments": {
             "hashes": [
-                "sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297",
-                "sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717"
+                "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c",
+                "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"
             ],
-            "markers": "python_version >= '3.6'",
-            "version": "==2.14.0"
+            "markers": "python_version >= '3.7'",
+            "version": "==2.15.1"
         },
         "six": {
             "hashes": [
@@ -587,14 +560,6 @@
             ],
             "version": "==0.6.2"
         },
-        "tomli": {
-            "hashes": [
-                "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
-                "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
-            ],
-            "markers": "python_version < '3.11'",
-            "version": "==2.0.1"
-        },
         "traitlets": {
             "hashes": [
                 "sha256:9e6ec080259b9a5940c797d58b613b5e31441c2257b87c2e795c5228ae80d2d8",

+ 32 - 0
docker/test/tests/test_capi_whitelists.py

@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+
+from http import HTTPStatus
+import yaml
+
+import pytest
+
+pytestmark = pytest.mark.docker
+
+
+def test_capi_whitelists(crowdsec, tmp_path_factory, flavor,):
+    """Test CAPI_WHITELISTS_PATH"""
+    env = {
+        "CAPI_WHITELISTS_PATH": "/path/to/whitelists.yaml"
+    }
+
+    whitelists = tmp_path_factory.mktemp("whitelists")
+    with open(whitelists / "whitelists.yaml", "w") as f:
+        yaml.dump({"ips": ["1.2.3.4", "2.3.4.5"], "cidrs": ["1.2.3.0/24"]}, f)
+
+    volumes = {
+        whitelists / "whitelists.yaml": {"bind": "/path/to/whitelists.yaml", "mode": "ro"}
+    }
+
+    with crowdsec(flavor=flavor, environment=env, volumes=volumes) as cs:
+        cs.wait_for_log("*Starting processing data*")
+        cs.wait_for_http(8080, '/health', want_status=HTTPStatus.OK)
+        res = cs.cont.exec_run(f'cscli config show-yaml')
+        assert res.exit_code == 0
+        stdout = res.output.decode()
+        y = yaml.safe_load(stdout)
+        assert y['api']['server']['capi_whitelists_path'] == '/path/to/whitelists.yaml'

+ 7 - 5
go.mod

@@ -48,11 +48,11 @@ require (
 	github.com/prometheus/client_model v0.3.0
 	github.com/prometheus/prom2json v1.3.0
 	github.com/r3labs/diff/v2 v2.14.1
-	github.com/sirupsen/logrus v1.9.0
+	github.com/sirupsen/logrus v1.9.2
 	github.com/spf13/cobra v1.7.0
-	github.com/stretchr/testify v1.8.2
-	golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
-	golang.org/x/mod v0.8.0
+	github.com/stretchr/testify v1.8.3
+	golang.org/x/crypto v0.1.0
+	golang.org/x/mod v0.6.0
 	google.golang.org/grpc v1.47.0
 	google.golang.org/protobuf v1.28.1
 	gopkg.in/natefinch/lumberjack.v2 v2.2.1
@@ -72,6 +72,7 @@ require (
 	github.com/cespare/xxhash/v2 v2.1.2
 	github.com/corazawaf/coraza/v3 v3.0.0-rc.2
 	github.com/coreos/go-systemd/v22 v22.5.0
+	github.com/crowdsecurity/go-cs-lib v0.0.0-20230531105801-4c1535c2b3bd
 	github.com/goccy/go-yaml v1.9.7
 	github.com/gofrs/uuid v4.0.0+incompatible
 	github.com/golang-jwt/jwt/v4 v4.2.0
@@ -85,6 +86,7 @@ require (
 	github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c
 	github.com/umahmood/haversine v0.0.0-20151105152445-808ab04add26
 	github.com/wasilibs/go-re2 v0.2.1
+	golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc
 	golang.org/x/sys v0.7.0
 	gopkg.in/yaml.v3 v3.0.1
 	k8s.io/apiserver v0.22.5
@@ -103,7 +105,7 @@ require (
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/corazawaf/libinjection-go v0.1.2 // indirect
 	github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
-	github.com/docker/distribution v2.8.0+incompatible // indirect
+	github.com/docker/distribution v2.8.2+incompatible // indirect
 	github.com/docker/go-units v0.4.0 // indirect
 	github.com/gin-contrib/sse v0.1.0 // indirect
 	github.com/go-logr/logr v1.2.3 // indirect

+ 12 - 7
go.sum

@@ -174,6 +174,8 @@ github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
 github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/crowdsecurity/dlog v0.0.0-20170105205344-4fb5f8204f26 h1:r97WNVC30Uen+7WnLs4xDScS/Ex988+id2k6mDf8psU=
 github.com/crowdsecurity/dlog v0.0.0-20170105205344-4fb5f8204f26/go.mod h1:zpv7r+7KXwgVUZnUNjyP22zc/D7LKjyoY02weH2RBbk=
+github.com/crowdsecurity/go-cs-lib v0.0.0-20230531105801-4c1535c2b3bd h1:Y70ceDKAKYFXTnxEjXuBDSh07umvDhbX3PCCYhdtsZ0=
+github.com/crowdsecurity/go-cs-lib v0.0.0-20230531105801-4c1535c2b3bd/go.mod h1:9JJLSpGj1ZXnROV3xAcJvS/HTaUvuA8K3gGOpO4tfVc=
 github.com/crowdsecurity/grokky v0.2.1 h1:t4VYnDlAd0RjDM2SlILalbwfCrQxtJSMGdQOR0zwkE4=
 github.com/crowdsecurity/grokky v0.2.1/go.mod h1:33usDIYzGDsgX1kHAThCbseso6JuWNJXOzRQDGXHtWM=
 github.com/crowdsecurity/machineid v1.0.2 h1:wpkpsUghJF8Khtmn/tg6GxgdhLA1Xflerh5lirI+bdc=
@@ -185,8 +187,8 @@ github.com/dghubble/sling v1.3.0 h1:pZHjCJq4zJvc6qVQ5wN1jo5oNZlNE0+8T/h0XeXBUKU=
 github.com/dghubble/sling v1.3.0/go.mod h1:XXShWaBWKzNLhu2OxikSNFrlsvowtz4kyRuXUG7oQKY=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
-github.com/docker/distribution v2.8.0+incompatible h1:l9EaZDICImO1ngI+uTifW+ZYvvz7fKISBAKpg+MbWbY=
-github.com/docker/distribution v2.8.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
+github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
+github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
 github.com/docker/docker v20.10.24+incompatible h1:Ugvxm7a8+Gz6vqQYQQ2W7GYq5EUPaAiuPgIfVyI3dYE=
 github.com/docker/docker v20.10.24+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
@@ -847,8 +849,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
 github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
 github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
-github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
-github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y=
+github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
@@ -887,8 +889,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
-github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
+github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
 github.com/tetratelabs/wazero v1.0.0-rc.2 h1:OA3UUynnoqxrjCQ94mpAtdO4/oMxFQVNL2BXDMOc66Q=
 github.com/tetratelabs/wazero v1.0.0-rc.2/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ=
@@ -1016,8 +1018,9 @@ golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWP
 golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
 golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
+golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1028,6 +1031,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
 golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
+golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU=
+golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=

+ 12 - 4
mk/goversion.mk

@@ -1,7 +1,4 @@
 
-BUILD_REQUIRE_GO_MAJOR ?= 1
-BUILD_REQUIRE_GO_MINOR ?= 20
-
 BUILD_GOVERSION = $(subst go,,$(shell go env GOVERSION))
 
 go_major_minor = $(subst ., ,$(BUILD_GOVERSION))
@@ -10,8 +7,19 @@ GO_MINOR_VERSION = $(word 2, $(go_major_minor))
 
 GO_VERSION_VALIDATION_ERR_MSG = Golang version ($(BUILD_GOVERSION)) is not supported, please use at least $(BUILD_REQUIRE_GO_MAJOR).$(BUILD_REQUIRE_GO_MINOR)
 
+
 .PHONY: goversion
-goversion:
+goversion: $(if $(findstring devel,$(shell go env GOVERSION)),goversion_devel,goversion_check)
+
+
+.PHONY: goversion_devel
+goversion_devel:
+	$(warning WARNING: You are using a development version of Golang ($(BUILD_GOVERSION)) which is not supported. For production environments, use a stable version (at least $(BUILD_REQUIRE_GO_MAJOR).$(BUILD_REQUIRE_GO_MINOR)))
+	$(info )
+
+
+.PHONY: goversion_check
+goversion_check:
 ifneq ($(OS), Windows_NT)
 	@if [ $(GO_MAJOR_VERSION) -gt $(BUILD_REQUIRE_GO_MAJOR) ]; then \
 		exit 0; \

+ 5 - 0
mk/platform.mk

@@ -1,3 +1,8 @@
+
+BUILD_CODENAME ?= alphaga
+GOARCH ?= $(shell go env GOARCH)
+BUILD_TAG ?= $(shell git rev-parse HEAD)
+
 ifeq ($(OS), Windows_NT)
 	SHELL := pwsh.exe
 	.SHELLFLAGS := -NoProfile -Command

+ 0 - 2
mk/platform/freebsd.mk

@@ -1,5 +1,3 @@
 # FreeBSD specific
 
 MAKE=gmake
-
-$(info building for FreeBSD)

+ 0 - 2
mk/platform/linux.mk

@@ -1,5 +1,3 @@
 # Linux specific
 
 MAKE=make
-
-$(info Building for linux)

+ 0 - 2
mk/platform/openbsd.mk

@@ -1,5 +1,3 @@
 # OpenBSD specific
 
 MAKE=gmake
-
-$(info building for OpenBSD)

+ 0 - 3
mk/platform/unix_common.mk

@@ -6,12 +6,9 @@ MKDIR=mkdir -p
 
 # Go should not be required to run functional tests
 GOOS ?= $(shell go env GOOS)
-GOARCH ?= $(shell go env GOARCH)
 
 #Current versioning information from env
 BUILD_VERSION?=$(shell git describe --tags)
-BUILD_CODENAME="alphaga"
 BUILD_TIMESTAMP=$(shell date +%F"_"%T)
-BUILD_TAG?=$(shell git rev-parse HEAD)
 DEFAULT_CONFIGDIR?=/etc/crowdsec
 DEFAULT_DATADIR?=/var/lib/crowdsec/data

+ 0 - 7
mk/platform/windows.mk

@@ -4,16 +4,11 @@ MAKE=make
 GOOS=windows
 PREFIX=$(shell $$env:TEMP)
 
-GOOS ?= $(shell go env GOOS)
-GOARCH ?= $(shell go env GOARCH)
-
 #Current versioning information from env
 #BUILD_VERSION?=$(shell (Invoke-WebRequest -UseBasicParsing -Uri https://api.github.com/repos/crowdsecurity/crowdsec/releases/latest).Content | jq -r '.tag_name')
 #hardcode it till i find a workaround
 BUILD_VERSION?=$(shell git describe --tags $$(git rev-list --tags --max-count=1))
-BUILD_CODENAME?=alphaga
 BUILD_TIMESTAMP?=$(shell Get-Date -Format "yyyy-MM-dd_HH:mm:ss")
-BUILD_TAG?=$(shell git rev-parse HEAD)
 DEFAULT_CONFIGDIR?=C:\\ProgramData\\CrowdSec\\config
 DEFAULT_DATADIR?=C:\\ProgramData\\CrowdSec\\data
 
@@ -23,5 +18,3 @@ CP=Copy-Item
 CPR=Copy-Item -Recurse
 MKDIR=New-Item -ItemType directory
 WIN_IGNORE_ERR=; exit 0
-
-$(info Building for windows)

+ 4 - 2
pkg/acquisition/acquisition.go

@@ -15,6 +15,8 @@ import (
 	tomb "gopkg.in/tomb.v2"
 	"gopkg.in/yaml.v2"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/trace"
+
 	"github.com/crowdsecurity/crowdsec/pkg/acquisition/configuration"
 	cloudwatchacquisition "github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/cloudwatch"
 	dockeracquisition "github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/docker"
@@ -243,7 +245,7 @@ func GetMetrics(sources []DataSource, aggregated bool) error {
 }
 
 func transform(transformChan chan types.Event, output chan types.Event, AcquisTomb *tomb.Tomb, transformRuntime *vm.Program, logger *log.Entry) {
-	defer types.CatchPanic("crowdsec/acquis")
+	defer trace.CatchPanic("crowdsec/acquis")
 	logger.Infof("transformer started")
 	for {
 		select {
@@ -298,7 +300,7 @@ func StartAcquisition(sources []DataSource, output chan types.Event, AcquisTomb
 		log.Debugf("starting one source %d/%d ->> %T", i, len(sources), subsrc)
 
 		AcquisTomb.Go(func() error {
-			defer types.CatchPanic("crowdsec/acquis")
+			defer trace.CatchPanic("crowdsec/acquis")
 			var err error
 
 			outChan := output

+ 2 - 1
pkg/acquisition/acquisition_test.go

@@ -13,9 +13,10 @@ import (
 	tomb "gopkg.in/tomb.v2"
 	"gopkg.in/yaml.v2"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/cstest"
+
 	"github.com/crowdsecurity/crowdsec/pkg/acquisition/configuration"
 	"github.com/crowdsecurity/crowdsec/pkg/csconfig"
-	"github.com/crowdsecurity/crowdsec/pkg/cstest"
 	"github.com/crowdsecurity/crowdsec/pkg/types"
 )
 

+ 2 - 1
pkg/acquisition/modules/cloudwatch/cloudwatch_test.go

@@ -9,9 +9,10 @@ import (
 	"testing"
 	"time"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/cstest"
+
 	"github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
-	"github.com/crowdsecurity/crowdsec/pkg/cstest"
 	"github.com/crowdsecurity/crowdsec/pkg/types"
 	log "github.com/sirupsen/logrus"
 	"github.com/stretchr/testify/require"

+ 2 - 1
pkg/acquisition/modules/docker/docker_test.go

@@ -11,7 +11,8 @@ import (
 	"testing"
 	"time"
 
-	"github.com/crowdsecurity/crowdsec/pkg/cstest"
+	"github.com/crowdsecurity/go-cs-lib/pkg/cstest"
+
 	"github.com/crowdsecurity/crowdsec/pkg/types"
 	dockerTypes "github.com/docker/docker/api/types"
 	dockerContainer "github.com/docker/docker/api/types/container"

+ 7 - 4
pkg/acquisition/modules/file/file.go

@@ -14,6 +14,8 @@ import (
 	"strings"
 	"time"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/trace"
+
 	"github.com/fsnotify/fsnotify"
 	"github.com/nxadm/tail"
 	"github.com/pkg/errors"
@@ -39,6 +41,7 @@ type FileConfiguration struct {
 	Filename                          string
 	ForceInotify                      bool `yaml:"force_inotify"`
 	MaxBufferSize                     int  `yaml:"max_buffer_size"`
+	PollWithoutInotify                bool `yaml:"poll_without_inotify"`
 	configuration.DataSourceCommonCfg `yaml:",inline"`
 }
 
@@ -328,14 +331,14 @@ func (f *FileSource) StreamingAcquisition(out chan types.Event, t *tomb.Tomb) er
 			continue
 		}
 
-		tail, err := tail.TailFile(file, tail.Config{ReOpen: true, Follow: true, Poll: true, Location: &tail.SeekInfo{Offset: 0, Whence: io.SeekEnd}, Logger: log.NewEntry(log.StandardLogger())})
+		tail, err := tail.TailFile(file, tail.Config{ReOpen: true, Follow: true, Poll: f.config.PollWithoutInotify, Location: &tail.SeekInfo{Offset: 0, Whence: io.SeekEnd}, Logger: log.NewEntry(log.StandardLogger())})
 		if err != nil {
 			f.logger.Errorf("Could not start tailing file %s : %s", file, err)
 			continue
 		}
 		f.tails[file] = true
 		t.Go(func() error {
-			defer types.CatchPanic("crowdsec/acquis/file/live/fsnotify")
+			defer trace.CatchPanic("crowdsec/acquis/file/live/fsnotify")
 			return f.tailFile(out, t, tail)
 		})
 	}
@@ -411,14 +414,14 @@ func (f *FileSource) monitorNewFiles(out chan types.Event, t *tomb.Tomb) error {
 					continue
 				}
 				//Slightly different parameters for Location, as we want to read the first lines of the newly created file
-				tail, err := tail.TailFile(event.Name, tail.Config{ReOpen: true, Follow: true, Poll: true, Location: &tail.SeekInfo{Offset: 0, Whence: io.SeekStart}})
+				tail, err := tail.TailFile(event.Name, tail.Config{ReOpen: true, Follow: true, Poll: f.config.PollWithoutInotify, Location: &tail.SeekInfo{Offset: 0, Whence: io.SeekStart}})
 				if err != nil {
 					logger.Errorf("Could not start tailing file %s : %s", event.Name, err)
 					break
 				}
 				f.tails[event.Name] = true
 				t.Go(func() error {
-					defer types.CatchPanic("crowdsec/acquis/tailfile")
+					defer trace.CatchPanic("crowdsec/acquis/tailfile")
 					return f.tailFile(out, t, tail)
 				})
 			}

+ 2 - 1
pkg/acquisition/modules/file/file_test.go

@@ -13,8 +13,9 @@ import (
 	"github.com/stretchr/testify/require"
 	"gopkg.in/tomb.v2"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/cstest"
+
 	fileacquisition "github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/file"
-	"github.com/crowdsecurity/crowdsec/pkg/cstest"
 	"github.com/crowdsecurity/crowdsec/pkg/types"
 )
 

+ 4 - 2
pkg/acquisition/modules/journalctl/journalctl.go

@@ -15,6 +15,8 @@ import (
 	"gopkg.in/tomb.v2"
 	"gopkg.in/yaml.v2"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/trace"
+
 	"github.com/crowdsecurity/crowdsec/pkg/acquisition/configuration"
 	"github.com/crowdsecurity/crowdsec/pkg/types"
 )
@@ -257,7 +259,7 @@ func (j *JournalCtlSource) GetName() string {
 }
 
 func (j *JournalCtlSource) OneShotAcquisition(out chan types.Event, t *tomb.Tomb) error {
-	defer types.CatchPanic("crowdsec/acquis/journalctl/oneshot")
+	defer trace.CatchPanic("crowdsec/acquis/journalctl/oneshot")
 	err := j.runJournalCtl(out, t)
 	j.logger.Debug("Oneshot journalctl acquisition is done")
 	return err
@@ -266,7 +268,7 @@ func (j *JournalCtlSource) OneShotAcquisition(out chan types.Event, t *tomb.Tomb
 
 func (j *JournalCtlSource) StreamingAcquisition(out chan types.Event, t *tomb.Tomb) error {
 	t.Go(func() error {
-		defer types.CatchPanic("crowdsec/acquis/journalctl/streaming")
+		defer trace.CatchPanic("crowdsec/acquis/journalctl/streaming")
 		return j.runJournalCtl(out, t)
 	})
 	return nil

+ 2 - 1
pkg/acquisition/modules/journalctl/journalctl_test.go

@@ -8,7 +8,8 @@ import (
 	"testing"
 	"time"
 
-	"github.com/crowdsecurity/crowdsec/pkg/cstest"
+	"github.com/crowdsecurity/go-cs-lib/pkg/cstest"
+
 	"github.com/crowdsecurity/crowdsec/pkg/types"
 	log "github.com/sirupsen/logrus"
 	"github.com/sirupsen/logrus/hooks/test"

+ 10 - 2
pkg/acquisition/modules/kafka/kafka.go

@@ -17,6 +17,8 @@ import (
 	"gopkg.in/tomb.v2"
 	"gopkg.in/yaml.v2"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/trace"
+
 	"github.com/crowdsecurity/crowdsec/pkg/acquisition/configuration"
 	"github.com/crowdsecurity/crowdsec/pkg/types"
 )
@@ -190,7 +192,7 @@ func (k *KafkaSource) StreamingAcquisition(out chan types.Event, t *tomb.Tomb) e
 	k.logger.Infof("start reader on topic '%s'", k.Config.Topic)
 
 	t.Go(func() error {
-		defer types.CatchPanic("crowdsec/acquis/kafka/live")
+		defer trace.CatchPanic("crowdsec/acquis/kafka/live")
 		return k.RunReader(out, t)
 	})
 
@@ -212,7 +214,13 @@ func (kc *KafkaConfiguration) NewTLSConfig() (*tls.Config, error) {
 	if err != nil {
 		return &tlsConfig, err
 	}
-	caCertPool := x509.NewCertPool()
+	caCertPool, err := x509.SystemCertPool()
+	if err != nil {
+		return &tlsConfig, fmt.Errorf("unable to load system CA certificates: %w", err)
+	}
+	if caCertPool == nil {
+		caCertPool = x509.NewCertPool()
+	}
 	caCertPool.AppendCertsFromPEM(caCert)
 	tlsConfig.RootCAs = caCertPool
 

+ 2 - 1
pkg/acquisition/modules/kafka/kafka_test.go

@@ -8,7 +8,8 @@ import (
 	"testing"
 	"time"
 
-	"github.com/crowdsecurity/crowdsec/pkg/cstest"
+	"github.com/crowdsecurity/go-cs-lib/pkg/cstest"
+
 	"github.com/crowdsecurity/crowdsec/pkg/types"
 	"github.com/segmentio/kafka-go"
 	log "github.com/sirupsen/logrus"

+ 4 - 2
pkg/acquisition/modules/kinesis/kinesis.go

@@ -19,6 +19,8 @@ import (
 	"gopkg.in/tomb.v2"
 	"gopkg.in/yaml.v2"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/trace"
+
 	"github.com/crowdsecurity/crowdsec/pkg/acquisition/configuration"
 	"github.com/crowdsecurity/crowdsec/pkg/types"
 )
@@ -490,7 +492,7 @@ func (k *KinesisSource) ReadFromStream(out chan types.Event, t *tomb.Tomb) error
 		for _, shard := range shards.Shards {
 			shardId := *shard.ShardId
 			k.shardReaderTomb.Go(func() error {
-				defer types.CatchPanic("crowdsec/acquis/kinesis/streaming/shard")
+				defer trace.CatchPanic("crowdsec/acquis/kinesis/streaming/shard")
 				return k.ReadFromShard(out, shardId)
 			})
 		}
@@ -514,7 +516,7 @@ func (k *KinesisSource) ReadFromStream(out chan types.Event, t *tomb.Tomb) error
 
 func (k *KinesisSource) StreamingAcquisition(out chan types.Event, t *tomb.Tomb) error {
 	t.Go(func() error {
-		defer types.CatchPanic("crowdsec/acquis/kinesis/streaming")
+		defer trace.CatchPanic("crowdsec/acquis/kinesis/streaming")
 		if k.Config.UseEnhancedFanOut {
 			return k.EnhancedRead(out, t)
 		} else {

+ 2 - 1
pkg/acquisition/modules/kinesis/kinesis_test.go

@@ -12,10 +12,11 @@ import (
 	"testing"
 	"time"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/cstest"
+
 	"github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/aws/session"
 	"github.com/aws/aws-sdk-go/service/kinesis"
-	"github.com/crowdsecurity/crowdsec/pkg/cstest"
 	"github.com/crowdsecurity/crowdsec/pkg/types"
 	log "github.com/sirupsen/logrus"
 	"github.com/stretchr/testify/assert"

+ 3 - 1
pkg/acquisition/modules/kubernetesaudit/k8s_audit.go

@@ -8,6 +8,8 @@ import (
 	"net/http"
 	"strings"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/trace"
+
 	"github.com/crowdsecurity/crowdsec/pkg/acquisition/configuration"
 	"github.com/crowdsecurity/crowdsec/pkg/types"
 	"github.com/pkg/errors"
@@ -133,7 +135,7 @@ func (ka *KubernetesAuditSource) OneShotAcquisition(out chan types.Event, t *tom
 func (ka *KubernetesAuditSource) StreamingAcquisition(out chan types.Event, t *tomb.Tomb) error {
 	ka.outChan = out
 	t.Go(func() error {
-		defer types.CatchPanic("crowdsec/acquis/k8s-audit/live")
+		defer trace.CatchPanic("crowdsec/acquis/k8s-audit/live")
 		ka.logger.Infof("Starting k8s-audit server on %s:%d%s", ka.config.ListenAddr, ka.config.ListenPort, ka.config.WebhookPath)
 		t.Go(func() error {
 			err := ka.server.ListenAndServe()

+ 2 - 1
pkg/acquisition/modules/syslog/internal/parser/rfc5424/parse_test.go

@@ -4,8 +4,9 @@ import (
 	"testing"
 	"time"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/cstest"
+
 	"github.com/stretchr/testify/require"
-	"github.com/crowdsecurity/crowdsec/pkg/cstest"
 )
 
 func TestPri(t *testing.T) {

+ 3 - 1
pkg/acquisition/modules/syslog/syslog.go

@@ -12,6 +12,8 @@ import (
 	"gopkg.in/tomb.v2"
 	"gopkg.in/yaml.v2"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/trace"
+
 	"github.com/crowdsecurity/crowdsec/pkg/acquisition/configuration"
 	"github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/syslog/internal/parser/rfc3164"
 	"github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/syslog/internal/parser/rfc5424"
@@ -142,7 +144,7 @@ func (s *SyslogSource) StreamingAcquisition(out chan types.Event, t *tomb.Tomb)
 	}
 	s.serverTomb = s.server.StartServer()
 	t.Go(func() error {
-		defer types.CatchPanic("crowdsec/acquis/syslog/live")
+		defer trace.CatchPanic("crowdsec/acquis/syslog/live")
 		return s.handleSyslogMsg(out, t, c)
 	})
 	return nil

+ 2 - 1
pkg/acquisition/modules/syslog/syslog_test.go

@@ -7,7 +7,8 @@ import (
 	"testing"
 	"time"
 
-	"github.com/crowdsecurity/crowdsec/pkg/cstest"
+	"github.com/crowdsecurity/go-cs-lib/pkg/cstest"
+
 	"github.com/crowdsecurity/crowdsec/pkg/types"
 	log "github.com/sirupsen/logrus"
 	"gopkg.in/tomb.v2"

+ 3 - 1
pkg/acquisition/modules/wineventlog/wineventlog_windows.go

@@ -17,6 +17,8 @@ import (
 	"gopkg.in/tomb.v2"
 	"gopkg.in/yaml.v2"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/trace"
+
 	"github.com/crowdsecurity/crowdsec/pkg/acquisition/configuration"
 	"github.com/crowdsecurity/crowdsec/pkg/types"
 )
@@ -321,7 +323,7 @@ func (w *WinEventLogSource) CanRun() error {
 
 func (w *WinEventLogSource) StreamingAcquisition(out chan types.Event, t *tomb.Tomb) error {
 	t.Go(func() error {
-		defer types.CatchPanic("crowdsec/acquis/wineventlog/streaming")
+		defer trace.CatchPanic("crowdsec/acquis/wineventlog/streaming")
 		return w.getEvents(out, t)
 	})
 	return nil

+ 4 - 2
pkg/alertcontext/alertcontext.go

@@ -7,10 +7,12 @@ import (
 
 	"github.com/antonmedv/expr"
 	"github.com/antonmedv/expr/vm"
+	log "github.com/sirupsen/logrus"
+	"golang.org/x/exp/slices"
+
 	"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
 	"github.com/crowdsecurity/crowdsec/pkg/models"
 	"github.com/crowdsecurity/crowdsec/pkg/types"
-	log "github.com/sirupsen/logrus"
 )
 
 const (
@@ -131,7 +133,7 @@ func EventToContext(events []types.Event) (models.Meta, []error) {
 					errors = append(errors, fmt.Errorf("unexpected return type for %s : %T", key, output))
 					continue
 				}
-				if val != "" && !types.InSlice(val, tmpContext[key]) {
+				if val != "" && !slices.Contains(tmpContext[key], val) {
 					tmpContext[key] = append(tmpContext[key], val)
 				}
 			}

+ 6 - 5
pkg/apiclient/alerts_service_test.go

@@ -8,7 +8,8 @@ import (
 	"reflect"
 	"testing"
 
-	"github.com/crowdsecurity/crowdsec/pkg/cwversion"
+	"github.com/crowdsecurity/go-cs-lib/pkg/version"
+
 	"github.com/crowdsecurity/crowdsec/pkg/models"
 	log "github.com/sirupsen/logrus"
 	"github.com/stretchr/testify/assert"
@@ -31,7 +32,7 @@ func TestAlertsListAsMachine(t *testing.T) {
 	client, err := NewClient(&Config{
 		MachineID:     "test_login",
 		Password:      "test_password",
-		UserAgent:     fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+		UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
 		URL:           apiURL,
 		VersionPrefix: "v1",
 	})
@@ -233,7 +234,7 @@ func TestAlertsGetAsMachine(t *testing.T) {
 	client, err := NewClient(&Config{
 		MachineID:     "test_login",
 		Password:      "test_password",
-		UserAgent:     fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+		UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
 		URL:           apiURL,
 		VersionPrefix: "v1",
 	})
@@ -423,7 +424,7 @@ func TestAlertsCreateAsMachine(t *testing.T) {
 	client, err := NewClient(&Config{
 		MachineID:     "test_login",
 		Password:      "test_password",
-		UserAgent:     fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+		UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
 		URL:           apiURL,
 		VersionPrefix: "v1",
 	})
@@ -467,7 +468,7 @@ func TestAlertsDeleteAsMachine(t *testing.T) {
 	client, err := NewClient(&Config{
 		MachineID:     "test_login",
 		Password:      "test_password",
-		UserAgent:     fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+		UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
 		URL:           apiURL,
 		VersionPrefix: "v1",
 	})

+ 6 - 5
pkg/apiclient/auth_service_test.go

@@ -10,7 +10,8 @@ import (
 	"net/url"
 	"testing"
 
-	"github.com/crowdsecurity/crowdsec/pkg/cwversion"
+	"github.com/crowdsecurity/go-cs-lib/pkg/version"
+
 	"github.com/crowdsecurity/crowdsec/pkg/models"
 	log "github.com/sirupsen/logrus"
 	"github.com/stretchr/testify/assert"
@@ -86,7 +87,7 @@ func TestWatcherRegister(t *testing.T) {
 	clientconfig := Config{
 		MachineID:     "test_login",
 		Password:      "test_password",
-		UserAgent:     fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+		UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
 		URL:           apiURL,
 		VersionPrefix: "v1",
 	}
@@ -128,7 +129,7 @@ func TestWatcherAuth(t *testing.T) {
 	clientConfig := &Config{
 		MachineID:     "test_login",
 		Password:      "test_password",
-		UserAgent:     fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+		UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
 		URL:           apiURL,
 		VersionPrefix: "v1",
 		Scenarios:     []string{"crowdsecurity/test"},
@@ -216,7 +217,7 @@ func TestWatcherUnregister(t *testing.T) {
 	mycfg := &Config{
 		MachineID:     "test_login",
 		Password:      "test_password",
-		UserAgent:     fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+		UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
 		URL:           apiURL,
 		VersionPrefix: "v1",
 		Scenarios:     []string{"crowdsecurity/test"},
@@ -270,7 +271,7 @@ func TestWatcherEnroll(t *testing.T) {
 	mycfg := &Config{
 		MachineID:     "test_login",
 		Password:      "test_password",
-		UserAgent:     fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+		UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
 		URL:           apiURL,
 		VersionPrefix: "v1",
 		Scenarios:     []string{"crowdsecurity/test"},

+ 4 - 3
pkg/apiclient/client_http_test.go

@@ -8,7 +8,8 @@ import (
 	"testing"
 	"time"
 
-	"github.com/crowdsecurity/crowdsec/pkg/cwversion"
+	"github.com/crowdsecurity/go-cs-lib/pkg/version"
+
 	"github.com/stretchr/testify/assert"
 )
 
@@ -23,7 +24,7 @@ func TestNewRequestInvalid(t *testing.T) {
 	client, err := NewClient(&Config{
 		MachineID:     "test_login",
 		Password:      "test_password",
-		UserAgent:     fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+		UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
 		URL:           apiURL,
 		VersionPrefix: "v1",
 	})
@@ -56,7 +57,7 @@ func TestNewRequestTimeout(t *testing.T) {
 	client, err := NewClient(&Config{
 		MachineID:     "test_login",
 		Password:      "test_password",
-		UserAgent:     fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+		UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
 		URL:           apiURL,
 		VersionPrefix: "v1",
 	})

+ 7 - 6
pkg/apiclient/client_test.go

@@ -11,8 +11,9 @@ import (
 
 	"github.com/stretchr/testify/assert"
 
-	"github.com/crowdsecurity/crowdsec/pkg/cwversion"
 	log "github.com/sirupsen/logrus"
+
+	"github.com/crowdsecurity/go-cs-lib/pkg/version"
 )
 
 /*this is a ripoff of google/go-github approach :
@@ -55,7 +56,7 @@ func TestNewClientOk(t *testing.T) {
 	client, err := NewClient(&Config{
 		MachineID:     "test_login",
 		Password:      "test_password",
-		UserAgent:     fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+		UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
 		URL:           apiURL,
 		VersionPrefix: "v1",
 	})
@@ -92,7 +93,7 @@ func TestNewClientKo(t *testing.T) {
 	client, err := NewClient(&Config{
 		MachineID:     "test_login",
 		Password:      "test_password",
-		UserAgent:     fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+		UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
 		URL:           apiURL,
 		VersionPrefix: "v1",
 	})
@@ -143,7 +144,7 @@ func TestNewClientRegisterKO(t *testing.T) {
 	_, err = RegisterClient(&Config{
 		MachineID:     "test_login",
 		Password:      "test_password",
-		UserAgent:     fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+		UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
 		URL:           apiURL,
 		VersionPrefix: "v1",
 	}, &http.Client{})
@@ -173,7 +174,7 @@ func TestNewClientRegisterOK(t *testing.T) {
 	client, err := RegisterClient(&Config{
 		MachineID:     "test_login",
 		Password:      "test_password",
-		UserAgent:     fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+		UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
 		URL:           apiURL,
 		VersionPrefix: "v1",
 	}, &http.Client{})
@@ -201,7 +202,7 @@ func TestNewClientBadAnswer(t *testing.T) {
 	_, err = RegisterClient(&Config{
 		MachineID:     "test_login",
 		Password:      "test_password",
-		UserAgent:     fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+		UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
 		URL:           apiURL,
 		VersionPrefix: "v1",
 	}, &http.Client{})

+ 7 - 5
pkg/apiclient/decisions_service.go

@@ -6,6 +6,8 @@ import (
 	"fmt"
 	"net/http"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/ptr"
+
 	"github.com/crowdsecurity/crowdsec/pkg/models"
 	"github.com/crowdsecurity/crowdsec/pkg/modelscapi"
 	"github.com/crowdsecurity/crowdsec/pkg/types"
@@ -102,10 +104,10 @@ func (s *DecisionsService) GetDecisionsFromGroups(decisionsGroups []*modelscapi.
 			partialDecisions[idx] = &models.Decision{
 				Scenario: decisionsGroup.Scenario,
 				Scope:    decisionsGroup.Scope,
-				Type:     types.StrPtr(types.DecisionTypeBan),
+				Type:     ptr.Of(types.DecisionTypeBan),
 				Value:    decision.Value,
 				Duration: decision.Duration,
-				Origin:   types.StrPtr(types.CAPIOrigin),
+				Origin:   ptr.Of(types.CAPIOrigin),
 			}
 		}
 		decisions = append(decisions, partialDecisions...)
@@ -138,10 +140,10 @@ func (s *DecisionsService) FetchV3Decisions(ctx context.Context, url string) (*m
 			partialDecisions[idx] = &models.Decision{
 				Scenario: &scenarioDeleted,
 				Scope:    decisionsGroup.Scope,
-				Type:     types.StrPtr(types.DecisionTypeBan),
+				Type:     ptr.Of(types.DecisionTypeBan),
 				Value:    &decision,
 				Duration: &durationDeleted,
-				Origin:   types.StrPtr(types.CAPIOrigin),
+				Origin:   ptr.Of(types.CAPIOrigin),
 			}
 		}
 		v2Decisions.Deleted = append(v2Decisions.Deleted, partialDecisions...)
@@ -210,7 +212,7 @@ func (s *DecisionsService) GetDecisionsFromBlocklist(ctx context.Context, blockl
 			Type:     blocklist.Remediation,
 			Value:    &decision,
 			Duration: blocklist.Duration,
-			Origin:   types.StrPtr(types.ListOrigin),
+			Origin:   ptr.Of(types.ListOrigin),
 		})
 	}
 

+ 6 - 5
pkg/apiclient/decisions_service_test.go

@@ -8,10 +8,11 @@ import (
 	"reflect"
 	"testing"
 
-	"github.com/crowdsecurity/crowdsec/pkg/cwversion"
+	"github.com/crowdsecurity/go-cs-lib/pkg/ptr"
+	"github.com/crowdsecurity/go-cs-lib/pkg/version"
+
 	"github.com/crowdsecurity/crowdsec/pkg/models"
 	"github.com/crowdsecurity/crowdsec/pkg/modelscapi"
-	"github.com/crowdsecurity/crowdsec/pkg/types"
 	log "github.com/sirupsen/logrus"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -460,7 +461,7 @@ func TestDecisionsFromBlocklist(t *testing.T) {
 		Remediation: &tremediationBlocklist,
 		Name:        &tnameBlocklist,
 		Duration:    &tdurationBlocklist,
-	}, types.StrPtr("Sun, 01 Jan 2023 01:01:01 GMT"))
+	}, ptr.Of("Sun, 01 Jan 2023 01:01:01 GMT"))
 	require.NoError(t, err)
 	assert.False(t, isModified)
 	_, isModified, err = newcli.Decisions.GetDecisionsFromBlocklist(context.Background(), &modelscapi.BlocklistLink{
@@ -469,7 +470,7 @@ func TestDecisionsFromBlocklist(t *testing.T) {
 		Remediation: &tremediationBlocklist,
 		Name:        &tnameBlocklist,
 		Duration:    &tdurationBlocklist,
-	}, types.StrPtr("Mon, 02 Jan 2023 01:01:01 GMT"))
+	}, ptr.Of("Mon, 02 Jan 2023 01:01:01 GMT"))
 	require.NoError(t, err)
 	assert.True(t, isModified)
 }
@@ -495,7 +496,7 @@ func TestDeleteDecisions(t *testing.T) {
 	client, err := NewClient(&Config{
 		MachineID:     "test_login",
 		Password:      "test_password",
-		UserAgent:     fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+		UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
 		URL:           apiURL,
 		VersionPrefix: "v1",
 	})

+ 3 - 2
pkg/apiclient/heartbeat.go

@@ -6,9 +6,10 @@ import (
 	"net/http"
 	"time"
 
-	"github.com/crowdsecurity/crowdsec/pkg/types"
 	log "github.com/sirupsen/logrus"
 	tomb "gopkg.in/tomb.v2"
+
+	"github.com/crowdsecurity/go-cs-lib/pkg/trace"
 )
 
 type HeartBeatService service
@@ -32,7 +33,7 @@ func (h *HeartBeatService) Ping(ctx context.Context) (bool, *Response, error) {
 
 func (h *HeartBeatService) StartHeartBeat(ctx context.Context, t *tomb.Tomb) {
 	t.Go(func() error {
-		defer types.CatchPanic("crowdsec/apiClient/heartbeat")
+		defer trace.CatchPanic("crowdsec/apiClient/heartbeat")
 		hbTimer := time.NewTicker(1 * time.Minute)
 		for {
 			select {

+ 5 - 5
pkg/apiserver/alerts_test.go

@@ -28,7 +28,7 @@ type LAPI struct {
 
 func SetupLAPITest(t *testing.T) LAPI {
 	t.Helper()
-	router, loginResp, config, err := InitMachineTest()
+	router, loginResp, config, err := InitMachineTest(t)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -68,8 +68,8 @@ func (l *LAPI) RecordResponse(verb string, url string, body *strings.Reader, aut
 	return w
 }
 
-func InitMachineTest() (*gin.Engine, models.WatcherAuthResponse, csconfig.Config, error) {
-	router, config, err := NewAPITest()
+func InitMachineTest(t *testing.T) (*gin.Engine, models.WatcherAuthResponse, csconfig.Config, error) {
+	router, config, err := NewAPITest(t)
 	if err != nil {
 		return nil, models.WatcherAuthResponse{}, config, fmt.Errorf("unable to run local API: %s", err)
 	}
@@ -151,7 +151,7 @@ func TestCreateAlert(t *testing.T) {
 
 func TestCreateAlertChannels(t *testing.T) {
 
-	apiServer, config, err := NewAPIServer()
+	apiServer, config, err := NewAPIServer(t)
 	if err != nil {
 		log.Fatalln(err)
 	}
@@ -443,7 +443,7 @@ func TestDeleteAlertByID(t *testing.T) {
 }
 
 func TestDeleteAlertTrustedIPS(t *testing.T) {
-	cfg := LoadTestConfig()
+	cfg := LoadTestConfig(t)
 	// IPv6 mocking doesn't seem to work.
 	// cfg.API.Server.TrustedIPs = []string{"1.2.3.4", "1.2.4.0/24", "::"}
 	cfg.API.Server.TrustedIPs = []string{"1.2.3.4", "1.2.4.0/24"}

+ 1 - 1
pkg/apiserver/api_key_test.go

@@ -11,7 +11,7 @@ import (
 )
 
 func TestAPIKey(t *testing.T) {
-	router, config, err := NewAPITest()
+	router, config, err := NewAPITest(t)
 	if err != nil {
 		log.Fatalf("unable to run local API: %s", err)
 	}

+ 35 - 31
pkg/apiserver/apic.go

@@ -15,11 +15,15 @@ import (
 	"github.com/go-openapi/strfmt"
 	"github.com/pkg/errors"
 	log "github.com/sirupsen/logrus"
+	"golang.org/x/exp/slices"
 	"gopkg.in/tomb.v2"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/ptr"
+	"github.com/crowdsecurity/go-cs-lib/pkg/trace"
+	"github.com/crowdsecurity/go-cs-lib/pkg/version"
+
 	"github.com/crowdsecurity/crowdsec/pkg/apiclient"
 	"github.com/crowdsecurity/crowdsec/pkg/csconfig"
-	"github.com/crowdsecurity/crowdsec/pkg/cwversion"
 	"github.com/crowdsecurity/crowdsec/pkg/database"
 	"github.com/crowdsecurity/crowdsec/pkg/database/ent"
 	"github.com/crowdsecurity/crowdsec/pkg/database/ent/alert"
@@ -81,7 +85,7 @@ func (a *apic) FetchScenariosListFromDB() ([]string, error) {
 		machineScenarios := strings.Split(v.Scenarios, ",")
 		log.Debugf("%d scenarios for machine %d", len(machineScenarios), v.ID)
 		for _, sv := range machineScenarios {
-			if !types.InSlice(sv, scenarios) && sv != "" {
+			if !slices.Contains(scenarios, sv) && sv != "" {
 				scenarios = append(scenarios, sv)
 			}
 		}
@@ -94,15 +98,15 @@ func decisionsToApiDecisions(decisions []*models.Decision) models.AddSignalsRequ
 	apiDecisions := models.AddSignalsRequestItemDecisions{}
 	for _, decision := range decisions {
 		x := &models.AddSignalsRequestItemDecisionsItem{
-			Duration: types.StrPtr(*decision.Duration),
+			Duration: ptr.Of(*decision.Duration),
 			ID:       new(int64),
-			Origin:   types.StrPtr(*decision.Origin),
-			Scenario: types.StrPtr(*decision.Scenario),
-			Scope:    types.StrPtr(*decision.Scope),
+			Origin:   ptr.Of(*decision.Origin),
+			Scenario: ptr.Of(*decision.Scenario),
+			Scope:    ptr.Of(*decision.Scope),
 			//Simulated: *decision.Simulated,
-			Type:  types.StrPtr(*decision.Type),
+			Type:  ptr.Of(*decision.Type),
 			Until: decision.Until,
-			Value: types.StrPtr(*decision.Value),
+			Value: ptr.Of(*decision.Value),
 			UUID:  decision.UUID,
 		}
 		*x.ID = decision.ID
@@ -193,7 +197,7 @@ func NewAPIC(config *csconfig.OnlineApiClientCfg, dbClient *database.Client, con
 	ret.apiClient, err = apiclient.NewClient(&apiclient.Config{
 		MachineID:      config.Credentials.Login,
 		Password:       password,
-		UserAgent:      fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+		UserAgent:      fmt.Sprintf("crowdsec/%s", version.String()),
 		URL:            apiURL,
 		PapiURL:        papiURL,
 		VersionPrefix:  "v3",
@@ -231,7 +235,7 @@ func NewAPIC(config *csconfig.OnlineApiClientCfg, dbClient *database.Client, con
 
 // keep track of all alerts in cache and push it to CAPI every PushInterval.
 func (a *apic) Push() error {
-	defer types.CatchPanic("lapi/pushToAPIC")
+	defer trace.CatchPanic("lapi/pushToAPIC")
 
 	var cache models.AddSignalsRequest
 	ticker := time.NewTicker(a.pushIntervalFirst)
@@ -427,7 +431,7 @@ func (a *apic) HandleDeletedDecisionsV3(deletedDecisions []*modelscapi.GetDecisi
 			if err != nil {
 				return 0, errors.Wrapf(err, "converting db ret %d", dbCliDel)
 			}
-			updateCounterForDecision(delete_counters, types.StrPtr(types.CAPIOrigin), nil, dbCliDel)
+			updateCounterForDecision(delete_counters, ptr.Of(types.CAPIOrigin), nil, dbCliDel)
 			nbDeleted += dbCliDel
 		}
 	}
@@ -473,26 +477,26 @@ func createAlertsForDecisions(decisions []*models.Decision) []*models.Alert {
 func createAlertForDecision(decision *models.Decision) *models.Alert {
 	newAlert := &models.Alert{}
 	newAlert.Source = &models.Source{}
-	newAlert.Source.Scope = types.StrPtr("")
+	newAlert.Source.Scope = ptr.Of("")
 	if *decision.Origin == types.CAPIOrigin { //to make things more user friendly, we replace CAPI with community-blocklist
-		newAlert.Scenario = types.StrPtr(types.CAPIOrigin)
-		newAlert.Source.Scope = types.StrPtr(types.CAPIOrigin)
+		newAlert.Scenario = ptr.Of(types.CAPIOrigin)
+		newAlert.Source.Scope = ptr.Of(types.CAPIOrigin)
 	} else if *decision.Origin == types.ListOrigin {
-		newAlert.Scenario = types.StrPtr(*decision.Scenario)
-		newAlert.Source.Scope = types.StrPtr(types.ListOrigin)
+		newAlert.Scenario = ptr.Of(*decision.Scenario)
+		newAlert.Source.Scope = ptr.Of(types.ListOrigin)
 	} else {
 		log.Warningf("unknown origin %s", *decision.Origin)
 	}
-	newAlert.Message = types.StrPtr("")
-	newAlert.Source.Value = types.StrPtr("")
-	newAlert.StartAt = types.StrPtr(time.Now().UTC().Format(time.RFC3339))
-	newAlert.StopAt = types.StrPtr(time.Now().UTC().Format(time.RFC3339))
-	newAlert.Capacity = types.Int32Ptr(0)
-	newAlert.Simulated = types.BoolPtr(false)
-	newAlert.EventsCount = types.Int32Ptr(0)
-	newAlert.Leakspeed = types.StrPtr("")
-	newAlert.ScenarioHash = types.StrPtr("")
-	newAlert.ScenarioVersion = types.StrPtr("")
+	newAlert.Message = ptr.Of("")
+	newAlert.Source.Value = ptr.Of("")
+	newAlert.StartAt = ptr.Of(time.Now().UTC().Format(time.RFC3339))
+	newAlert.StopAt = ptr.Of(time.Now().UTC().Format(time.RFC3339))
+	newAlert.Capacity = ptr.Of(int32(0))
+	newAlert.Simulated = ptr.Of(false)
+	newAlert.EventsCount = ptr.Of(int32(0))
+	newAlert.Leakspeed = ptr.Of("")
+	newAlert.ScenarioHash = ptr.Of("")
+	newAlert.ScenarioVersion = ptr.Of("")
 	newAlert.MachineID = database.CapiMachineID
 	return newAlert
 }
@@ -769,16 +773,16 @@ func (a *apic) UpdateBlocklists(links *modelscapi.GetDecisionsStreamResponseLink
 func setAlertScenario(add_counters map[string]map[string]int, delete_counters map[string]map[string]int, alert *models.Alert) *models.Alert {
 	if *alert.Source.Scope == types.CAPIOrigin {
 		*alert.Source.Scope = SCOPE_CAPI_ALIAS_ALIAS
-		alert.Scenario = types.StrPtr(fmt.Sprintf("update : +%d/-%d IPs", add_counters[types.CAPIOrigin]["all"], delete_counters[types.CAPIOrigin]["all"]))
+		alert.Scenario = ptr.Of(fmt.Sprintf("update : +%d/-%d IPs", add_counters[types.CAPIOrigin]["all"], delete_counters[types.CAPIOrigin]["all"]))
 	} else if *alert.Source.Scope == types.ListOrigin {
 		*alert.Source.Scope = fmt.Sprintf("%s:%s", types.ListOrigin, *alert.Scenario)
-		alert.Scenario = types.StrPtr(fmt.Sprintf("update : +%d/-%d IPs", add_counters[types.ListOrigin][*alert.Scenario], delete_counters[types.ListOrigin][*alert.Scenario]))
+		alert.Scenario = ptr.Of(fmt.Sprintf("update : +%d/-%d IPs", add_counters[types.ListOrigin][*alert.Scenario], delete_counters[types.ListOrigin][*alert.Scenario]))
 	}
 	return alert
 }
 
 func (a *apic) Pull() error {
-	defer types.CatchPanic("lapi/pullFromAPIC")
+	defer trace.CatchPanic("lapi/pullFromAPIC")
 
 	toldOnce := false
 	for {
@@ -820,7 +824,7 @@ func (a *apic) Pull() error {
 
 func (a *apic) GetMetrics() (*models.Metrics, error) {
 	metric := &models.Metrics{
-		ApilVersion: types.StrPtr(cwversion.VersionStr()),
+		ApilVersion: ptr.Of(version.String()),
 		Machines:    make([]*models.MetricsAgentInfo, 0),
 		Bouncers:    make([]*models.MetricsBouncerInfo, 0),
 	}
@@ -861,7 +865,7 @@ func (a *apic) GetMetrics() (*models.Metrics, error) {
 }
 
 func (a *apic) SendMetrics(stop chan (bool)) {
-	defer types.CatchPanic("lapi/metricsToAPIC")
+	defer trace.CatchPanic("lapi/metricsToAPIC")
 
 	ticker := time.NewTicker(a.metricsIntervalFirst)
 

+ 144 - 142
pkg/apiserver/apic_test.go

@@ -20,10 +20,12 @@ import (
 	"github.com/stretchr/testify/require"
 	"gopkg.in/tomb.v2"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/cstest"
+	"github.com/crowdsecurity/go-cs-lib/pkg/ptr"
+	"github.com/crowdsecurity/go-cs-lib/pkg/version"
+
 	"github.com/crowdsecurity/crowdsec/pkg/apiclient"
 	"github.com/crowdsecurity/crowdsec/pkg/csconfig"
-	"github.com/crowdsecurity/crowdsec/pkg/cstest"
-	"github.com/crowdsecurity/crowdsec/pkg/cwversion"
 	"github.com/crowdsecurity/crowdsec/pkg/database"
 	"github.com/crowdsecurity/crowdsec/pkg/database/ent/decision"
 	"github.com/crowdsecurity/crowdsec/pkg/database/ent/machine"
@@ -59,10 +61,10 @@ func getAPIC(t *testing.T) *apic {
 		metricsTomb:  tomb.Tomb{},
 		scenarioList: make([]string, 0),
 		consoleConfig: &csconfig.ConsoleConfig{
-			ShareManualDecisions:  types.BoolPtr(false),
-			ShareTaintedScenarios: types.BoolPtr(false),
-			ShareCustomScenarios:  types.BoolPtr(false),
-			ShareContext:          types.BoolPtr(false),
+			ShareManualDecisions:  ptr.Of(false),
+			ShareTaintedScenarios: ptr.Of(false),
+			ShareCustomScenarios:  ptr.Of(false),
+			ShareContext:          ptr.Of(false),
 		},
 		isPulling: make(chan bool, 1),
 	}
@@ -205,7 +207,7 @@ func TestNewAPIC(t *testing.T) {
 			action: func() {},
 			args: args{
 				dbClient:      getDBClient(t),
-				consoleConfig: LoadTestConfig().API.Server.ConsoleConfig,
+				consoleConfig: LoadTestConfig(t).API.Server.ConsoleConfig,
 			},
 		},
 		{
@@ -213,7 +215,7 @@ func TestNewAPIC(t *testing.T) {
 			action: func() { testConfig.Credentials.URL = "foobar http://" },
 			args: args{
 				dbClient:      getDBClient(t),
-				consoleConfig: LoadTestConfig().API.Server.ConsoleConfig,
+				consoleConfig: LoadTestConfig(t).API.Server.ConsoleConfig,
 			},
 			expectedErr: "first path segment in URL cannot contain colon",
 		},
@@ -265,11 +267,11 @@ func TestAPICHandleDeletedDecisions(t *testing.T) {
 	assertTotalDecisionCount(t, api.dbClient, 2)
 
 	nbDeleted, err := api.HandleDeletedDecisions([]*models.Decision{{
-		Value:    types.StrPtr("1.2.3.4"),
-		Origin:   types.StrPtr(types.CAPIOrigin),
+		Value:    ptr.Of("1.2.3.4"),
+		Origin:   ptr.Of(types.CAPIOrigin),
 		Type:     &decision1.Type,
-		Scenario: types.StrPtr("crowdsec/test"),
-		Scope:    types.StrPtr("IP"),
+		Scenario: ptr.Of("crowdsec/test"),
+		Scope:    ptr.Of("IP"),
 	}}, deleteCounters)
 
 	assert.NoError(t, err)
@@ -293,7 +295,7 @@ func TestAPICGetMetrics(t *testing.T) {
 			machineIDs: []string{},
 			bouncers:   []string{},
 			expectedMetric: &models.Metrics{
-				ApilVersion: types.StrPtr(cwversion.VersionStr()),
+				ApilVersion: ptr.Of(version.String()),
 				Bouncers:    []*models.MetricsBouncerInfo{},
 				Machines:    []*models.MetricsAgentInfo{},
 			},
@@ -303,7 +305,7 @@ func TestAPICGetMetrics(t *testing.T) {
 			machineIDs: []string{"a", "b", "c"},
 			bouncers:   []string{"1", "2", "3"},
 			expectedMetric: &models.Metrics{
-				ApilVersion: types.StrPtr(cwversion.VersionStr()),
+				ApilVersion: ptr.Of(version.String()),
 				Bouncers: []*models.MetricsBouncerInfo{
 					{
 						CustomName: "1",
@@ -374,23 +376,23 @@ func TestAPICGetMetrics(t *testing.T) {
 
 func TestCreateAlertsForDecision(t *testing.T) {
 	httpBfDecisionList := &models.Decision{
-		Origin:   types.StrPtr(types.ListOrigin),
-		Scenario: types.StrPtr("crowdsecurity/http-bf"),
+		Origin:   ptr.Of(types.ListOrigin),
+		Scenario: ptr.Of("crowdsecurity/http-bf"),
 	}
 
 	sshBfDecisionList := &models.Decision{
-		Origin:   types.StrPtr(types.ListOrigin),
-		Scenario: types.StrPtr("crowdsecurity/ssh-bf"),
+		Origin:   ptr.Of(types.ListOrigin),
+		Scenario: ptr.Of("crowdsecurity/ssh-bf"),
 	}
 
 	httpBfDecisionCommunity := &models.Decision{
-		Origin:   types.StrPtr(types.CAPIOrigin),
-		Scenario: types.StrPtr("crowdsecurity/http-bf"),
+		Origin:   ptr.Of(types.CAPIOrigin),
+		Scenario: ptr.Of("crowdsecurity/http-bf"),
 	}
 
 	sshBfDecisionCommunity := &models.Decision{
-		Origin:   types.StrPtr(types.CAPIOrigin),
-		Scenario: types.StrPtr("crowdsecurity/ssh-bf"),
+		Origin:   ptr.Of(types.CAPIOrigin),
+		Scenario: ptr.Of("crowdsecurity/ssh-bf"),
 	}
 	type args struct {
 		decisions []*models.Decision
@@ -453,27 +455,27 @@ func TestCreateAlertsForDecision(t *testing.T) {
 
 func TestFillAlertsWithDecisions(t *testing.T) {
 	httpBfDecisionCommunity := &models.Decision{
-		Origin:   types.StrPtr(types.CAPIOrigin),
-		Scenario: types.StrPtr("crowdsecurity/http-bf"),
-		Scope:    types.StrPtr("ip"),
+		Origin:   ptr.Of(types.CAPIOrigin),
+		Scenario: ptr.Of("crowdsecurity/http-bf"),
+		Scope:    ptr.Of("ip"),
 	}
 
 	sshBfDecisionCommunity := &models.Decision{
-		Origin:   types.StrPtr(types.CAPIOrigin),
-		Scenario: types.StrPtr("crowdsecurity/ssh-bf"),
-		Scope:    types.StrPtr("ip"),
+		Origin:   ptr.Of(types.CAPIOrigin),
+		Scenario: ptr.Of("crowdsecurity/ssh-bf"),
+		Scope:    ptr.Of("ip"),
 	}
 
 	httpBfDecisionList := &models.Decision{
-		Origin:   types.StrPtr(types.ListOrigin),
-		Scenario: types.StrPtr("crowdsecurity/http-bf"),
-		Scope:    types.StrPtr("ip"),
+		Origin:   ptr.Of(types.ListOrigin),
+		Scenario: ptr.Of("crowdsecurity/http-bf"),
+		Scope:    ptr.Of("ip"),
 	}
 
 	sshBfDecisionList := &models.Decision{
-		Origin:   types.StrPtr(types.ListOrigin),
-		Scenario: types.StrPtr("crowdsecurity/ssh-bf"),
-		Scope:    types.StrPtr("ip"),
+		Origin:   ptr.Of(types.ListOrigin),
+		Scenario: ptr.Of("crowdsecurity/ssh-bf"),
+		Scope:    ptr.Of("ip"),
 	}
 	type args struct {
 		alerts    []*models.Alert
@@ -572,58 +574,58 @@ func TestAPICWhitelists(t *testing.T) {
 							"9.9.9.9", // This is already present in DB
 							"9.1.9.9", // This not present in DB
 						},
-						Scope: types.StrPtr("Ip"),
+						Scope: ptr.Of("Ip"),
 					}, // This is already present in DB
 				},
 				New: modelscapi.GetDecisionsStreamResponseNew{
 					&modelscapi.GetDecisionsStreamResponseNewItem{
-						Scenario: types.StrPtr("crowdsecurity/test1"),
-						Scope:    types.StrPtr("Ip"),
+						Scenario: ptr.Of("crowdsecurity/test1"),
+						Scope:    ptr.Of("Ip"),
 						Decisions: []*modelscapi.GetDecisionsStreamResponseNewItemDecisionsItems0{
 							{
-								Value:    types.StrPtr("13.2.3.4"), //wl by cidr
-								Duration: types.StrPtr("24h"),
+								Value:    ptr.Of("13.2.3.4"), //wl by cidr
+								Duration: ptr.Of("24h"),
 							},
 						},
 					},
 
 					&modelscapi.GetDecisionsStreamResponseNewItem{
-						Scenario: types.StrPtr("crowdsecurity/test1"),
-						Scope:    types.StrPtr("Ip"),
+						Scenario: ptr.Of("crowdsecurity/test1"),
+						Scope:    ptr.Of("Ip"),
 						Decisions: []*modelscapi.GetDecisionsStreamResponseNewItemDecisionsItems0{
 							{
-								Value:    types.StrPtr("2.2.3.4"),
-								Duration: types.StrPtr("24h"),
+								Value:    ptr.Of("2.2.3.4"),
+								Duration: ptr.Of("24h"),
 							},
 						},
 					},
 					&modelscapi.GetDecisionsStreamResponseNewItem{
-						Scenario: types.StrPtr("crowdsecurity/test2"),
-						Scope:    types.StrPtr("Ip"),
+						Scenario: ptr.Of("crowdsecurity/test2"),
+						Scope:    ptr.Of("Ip"),
 						Decisions: []*modelscapi.GetDecisionsStreamResponseNewItemDecisionsItems0{
 							{
-								Value:    types.StrPtr("13.2.3.5"), //wl by cidr
-								Duration: types.StrPtr("24h"),
+								Value:    ptr.Of("13.2.3.5"), //wl by cidr
+								Duration: ptr.Of("24h"),
 							},
 						},
 					}, // These two are from community list.
 					&modelscapi.GetDecisionsStreamResponseNewItem{
-						Scenario: types.StrPtr("crowdsecurity/test1"),
-						Scope:    types.StrPtr("Ip"),
+						Scenario: ptr.Of("crowdsecurity/test1"),
+						Scope:    ptr.Of("Ip"),
 						Decisions: []*modelscapi.GetDecisionsStreamResponseNewItemDecisionsItems0{
 							{
-								Value:    types.StrPtr("6.2.3.4"),
-								Duration: types.StrPtr("24h"),
+								Value:    ptr.Of("6.2.3.4"),
+								Duration: ptr.Of("24h"),
 							},
 						},
 					},
 					&modelscapi.GetDecisionsStreamResponseNewItem{
-						Scenario: types.StrPtr("crowdsecurity/test1"),
-						Scope:    types.StrPtr("Ip"),
+						Scenario: ptr.Of("crowdsecurity/test1"),
+						Scope:    ptr.Of("Ip"),
 						Decisions: []*modelscapi.GetDecisionsStreamResponseNewItemDecisionsItems0{
 							{
-								Value:    types.StrPtr("9.2.3.4"), //wl by ip
-								Duration: types.StrPtr("24h"),
+								Value:    ptr.Of("9.2.3.4"), //wl by ip
+								Duration: ptr.Of("24h"),
 							},
 						},
 					},
@@ -631,18 +633,18 @@ func TestAPICWhitelists(t *testing.T) {
 				Links: &modelscapi.GetDecisionsStreamResponseLinks{
 					Blocklists: []*modelscapi.BlocklistLink{
 						{
-							URL:         types.StrPtr("http://api.crowdsec.net/blocklist1"),
-							Name:        types.StrPtr("blocklist1"),
-							Scope:       types.StrPtr("Ip"),
-							Remediation: types.StrPtr("ban"),
-							Duration:    types.StrPtr("24h"),
+							URL:         ptr.Of("http://api.crowdsec.net/blocklist1"),
+							Name:        ptr.Of("blocklist1"),
+							Scope:       ptr.Of("Ip"),
+							Remediation: ptr.Of("ban"),
+							Duration:    ptr.Of("24h"),
 						},
 						{
-							URL:         types.StrPtr("http://api.crowdsec.net/blocklist2"),
-							Name:        types.StrPtr("blocklist2"),
-							Scope:       types.StrPtr("Ip"),
-							Remediation: types.StrPtr("ban"),
-							Duration:    types.StrPtr("24h"),
+							URL:         ptr.Of("http://api.crowdsec.net/blocklist2"),
+							Name:        ptr.Of("blocklist2"),
+							Scope:       ptr.Of("Ip"),
+							Remediation: ptr.Of("ban"),
+							Duration:    ptr.Of("24h"),
 						},
 					},
 				},
@@ -661,7 +663,7 @@ func TestAPICWhitelists(t *testing.T) {
 	apic, err := apiclient.NewDefaultClient(
 		url,
 		"/api",
-		fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+		fmt.Sprintf("crowdsec/%s", version.String()),
 		nil,
 	)
 	require.NoError(t, err)
@@ -734,27 +736,27 @@ func TestAPICPullTop(t *testing.T) {
 							"9.9.9.9", // This is already present in DB
 							"9.1.9.9", // This not present in DB
 						},
-						Scope: types.StrPtr("Ip"),
+						Scope: ptr.Of("Ip"),
 					}, // This is already present in DB
 				},
 				New: modelscapi.GetDecisionsStreamResponseNew{
 					&modelscapi.GetDecisionsStreamResponseNewItem{
-						Scenario: types.StrPtr("crowdsecurity/test1"),
-						Scope:    types.StrPtr("Ip"),
+						Scenario: ptr.Of("crowdsecurity/test1"),
+						Scope:    ptr.Of("Ip"),
 						Decisions: []*modelscapi.GetDecisionsStreamResponseNewItemDecisionsItems0{
 							{
-								Value:    types.StrPtr("1.2.3.4"),
-								Duration: types.StrPtr("24h"),
+								Value:    ptr.Of("1.2.3.4"),
+								Duration: ptr.Of("24h"),
 							},
 						},
 					},
 					&modelscapi.GetDecisionsStreamResponseNewItem{
-						Scenario: types.StrPtr("crowdsecurity/test2"),
-						Scope:    types.StrPtr("Ip"),
+						Scenario: ptr.Of("crowdsecurity/test2"),
+						Scope:    ptr.Of("Ip"),
 						Decisions: []*modelscapi.GetDecisionsStreamResponseNewItemDecisionsItems0{
 							{
-								Value:    types.StrPtr("1.2.3.5"),
-								Duration: types.StrPtr("24h"),
+								Value:    ptr.Of("1.2.3.5"),
+								Duration: ptr.Of("24h"),
 							},
 						},
 					}, // These two are from community list.
@@ -762,18 +764,18 @@ func TestAPICPullTop(t *testing.T) {
 				Links: &modelscapi.GetDecisionsStreamResponseLinks{
 					Blocklists: []*modelscapi.BlocklistLink{
 						{
-							URL:         types.StrPtr("http://api.crowdsec.net/blocklist1"),
-							Name:        types.StrPtr("blocklist1"),
-							Scope:       types.StrPtr("Ip"),
-							Remediation: types.StrPtr("ban"),
-							Duration:    types.StrPtr("24h"),
+							URL:         ptr.Of("http://api.crowdsec.net/blocklist1"),
+							Name:        ptr.Of("blocklist1"),
+							Scope:       ptr.Of("Ip"),
+							Remediation: ptr.Of("ban"),
+							Duration:    ptr.Of("24h"),
 						},
 						{
-							URL:         types.StrPtr("http://api.crowdsec.net/blocklist2"),
-							Name:        types.StrPtr("blocklist2"),
-							Scope:       types.StrPtr("Ip"),
-							Remediation: types.StrPtr("ban"),
-							Duration:    types.StrPtr("24h"),
+							URL:         ptr.Of("http://api.crowdsec.net/blocklist2"),
+							Name:        ptr.Of("blocklist2"),
+							Scope:       ptr.Of("Ip"),
+							Remediation: ptr.Of("ban"),
+							Duration:    ptr.Of("24h"),
 						},
 					},
 				},
@@ -792,7 +794,7 @@ func TestAPICPullTop(t *testing.T) {
 	apic, err := apiclient.NewDefaultClient(
 		url,
 		"/api",
-		fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+		fmt.Sprintf("crowdsec/%s", version.String()),
 		nil,
 	)
 	require.NoError(t, err)
@@ -840,12 +842,12 @@ func TestAPICPullTopBLCacheFirstCall(t *testing.T) {
 			modelscapi.GetDecisionsStreamResponse{
 				New: modelscapi.GetDecisionsStreamResponseNew{
 					&modelscapi.GetDecisionsStreamResponseNewItem{
-						Scenario: types.StrPtr("crowdsecurity/test1"),
-						Scope:    types.StrPtr("Ip"),
+						Scenario: ptr.Of("crowdsecurity/test1"),
+						Scope:    ptr.Of("Ip"),
 						Decisions: []*modelscapi.GetDecisionsStreamResponseNewItemDecisionsItems0{
 							{
-								Value:    types.StrPtr("1.2.3.4"),
-								Duration: types.StrPtr("24h"),
+								Value:    ptr.Of("1.2.3.4"),
+								Duration: ptr.Of("24h"),
 							},
 						},
 					},
@@ -853,11 +855,11 @@ func TestAPICPullTopBLCacheFirstCall(t *testing.T) {
 				Links: &modelscapi.GetDecisionsStreamResponseLinks{
 					Blocklists: []*modelscapi.BlocklistLink{
 						{
-							URL:         types.StrPtr("http://api.crowdsec.net/blocklist1"),
-							Name:        types.StrPtr("blocklist1"),
-							Scope:       types.StrPtr("Ip"),
-							Remediation: types.StrPtr("ban"),
-							Duration:    types.StrPtr("24h"),
+							URL:         ptr.Of("http://api.crowdsec.net/blocklist1"),
+							Name:        ptr.Of("blocklist1"),
+							Scope:       ptr.Of("Ip"),
+							Remediation: ptr.Of("ban"),
+							Duration:    ptr.Of("24h"),
 						},
 					},
 				},
@@ -874,7 +876,7 @@ func TestAPICPullTopBLCacheFirstCall(t *testing.T) {
 	apic, err := apiclient.NewDefaultClient(
 		url,
 		"/api",
-		fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+		fmt.Sprintf("crowdsec/%s", version.String()),
 		nil,
 	)
 	require.NoError(t, err)
@@ -883,7 +885,7 @@ func TestAPICPullTopBLCacheFirstCall(t *testing.T) {
 	err = api.PullTop(false)
 	require.NoError(t, err)
 
-	blocklistConfigItemName := fmt.Sprintf("blocklist:%s:last_pull", *types.StrPtr("blocklist1"))
+	blocklistConfigItemName := "blocklist:blocklist1:last_pull"
 	lastPullTimestamp, err := api.dbClient.GetConfigItem(blocklistConfigItemName)
 	require.NoError(t, err)
 	assert.NotEqual(t, "", *lastPullTimestamp)
@@ -927,12 +929,12 @@ func TestAPICPullTopBLCacheForceCall(t *testing.T) {
 			modelscapi.GetDecisionsStreamResponse{
 				New: modelscapi.GetDecisionsStreamResponseNew{
 					&modelscapi.GetDecisionsStreamResponseNewItem{
-						Scenario: types.StrPtr("crowdsecurity/test1"),
-						Scope:    types.StrPtr("Ip"),
+						Scenario: ptr.Of("crowdsecurity/test1"),
+						Scope:    ptr.Of("Ip"),
 						Decisions: []*modelscapi.GetDecisionsStreamResponseNewItemDecisionsItems0{
 							{
-								Value:    types.StrPtr("1.2.3.4"),
-								Duration: types.StrPtr("24h"),
+								Value:    ptr.Of("1.2.3.4"),
+								Duration: ptr.Of("24h"),
 							},
 						},
 					},
@@ -940,11 +942,11 @@ func TestAPICPullTopBLCacheForceCall(t *testing.T) {
 				Links: &modelscapi.GetDecisionsStreamResponseLinks{
 					Blocklists: []*modelscapi.BlocklistLink{
 						{
-							URL:         types.StrPtr("http://api.crowdsec.net/blocklist1"),
-							Name:        types.StrPtr("blocklist1"),
-							Scope:       types.StrPtr("Ip"),
-							Remediation: types.StrPtr("ban"),
-							Duration:    types.StrPtr("24h"),
+							URL:         ptr.Of("http://api.crowdsec.net/blocklist1"),
+							Name:        ptr.Of("blocklist1"),
+							Scope:       ptr.Of("Ip"),
+							Remediation: ptr.Of("ban"),
+							Duration:    ptr.Of("24h"),
 						},
 					},
 				},
@@ -961,7 +963,7 @@ func TestAPICPullTopBLCacheForceCall(t *testing.T) {
 	apic, err := apiclient.NewDefaultClient(
 		url,
 		"/api",
-		fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+		fmt.Sprintf("crowdsec/%s", version.String()),
 		nil,
 	)
 	require.NoError(t, err)
@@ -981,10 +983,10 @@ func TestAPICPush(t *testing.T) {
 			name: "simple single alert",
 			alerts: []*models.Alert{
 				{
-					Scenario:        types.StrPtr("crowdsec/test"),
-					ScenarioHash:    types.StrPtr("certified"),
-					ScenarioVersion: types.StrPtr("v1.0"),
-					Simulated:       types.BoolPtr(false),
+					Scenario:        ptr.Of("crowdsec/test"),
+					ScenarioHash:    ptr.Of("certified"),
+					ScenarioVersion: ptr.Of("v1.0"),
+					Simulated:       ptr.Of(false),
 					Source:          &models.Source{},
 				},
 			},
@@ -994,10 +996,10 @@ func TestAPICPush(t *testing.T) {
 			name: "simulated alert is not pushed",
 			alerts: []*models.Alert{
 				{
-					Scenario:        types.StrPtr("crowdsec/test"),
-					ScenarioHash:    types.StrPtr("certified"),
-					ScenarioVersion: types.StrPtr("v1.0"),
-					Simulated:       types.BoolPtr(true),
+					Scenario:        ptr.Of("crowdsec/test"),
+					ScenarioHash:    ptr.Of("certified"),
+					ScenarioVersion: ptr.Of("v1.0"),
+					Simulated:       ptr.Of(true),
 					Source:          &models.Source{},
 				},
 			},
@@ -1010,10 +1012,10 @@ func TestAPICPush(t *testing.T) {
 				alerts := make([]*models.Alert, 100)
 				for i := 0; i < 100; i++ {
 					alerts[i] = &models.Alert{
-						Scenario:        types.StrPtr("crowdsec/test"),
-						ScenarioHash:    types.StrPtr("certified"),
-						ScenarioVersion: types.StrPtr("v1.0"),
-						Simulated:       types.BoolPtr(false),
+						Scenario:        ptr.Of("crowdsec/test"),
+						ScenarioHash:    ptr.Of("certified"),
+						ScenarioVersion: ptr.Of("v1.0"),
+						Simulated:       ptr.Of(false),
 						Source:          &models.Source{},
 					}
 				}
@@ -1036,7 +1038,7 @@ func TestAPICPush(t *testing.T) {
 			apic, err := apiclient.NewDefaultClient(
 				url,
 				"/api",
-				fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+				fmt.Sprintf("crowdsec/%s", version.String()),
 				nil,
 			)
 			require.NoError(t, err)
@@ -1111,7 +1113,7 @@ func TestAPICSendMetrics(t *testing.T) {
 			apiClient, err := apiclient.NewDefaultClient(
 				url,
 				"/api",
-				fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+				fmt.Sprintf("crowdsec/%s", version.String()),
 				nil,
 			)
 			require.NoError(t, err)
@@ -1179,7 +1181,7 @@ func TestAPICPull(t *testing.T) {
 			apic, err := apiclient.NewDefaultClient(
 				url,
 				"/api",
-				fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
+				fmt.Sprintf("crowdsec/%s", version.String()),
 				nil,
 			)
 			require.NoError(t, err)
@@ -1188,12 +1190,12 @@ func TestAPICPull(t *testing.T) {
 				modelscapi.GetDecisionsStreamResponse{
 					New: modelscapi.GetDecisionsStreamResponseNew{
 						&modelscapi.GetDecisionsStreamResponseNewItem{
-							Scenario: types.StrPtr("crowdsecurity/ssh-bf"),
-							Scope:    types.StrPtr("Ip"),
+							Scenario: ptr.Of("crowdsecurity/ssh-bf"),
+							Scope:    ptr.Of("Ip"),
 							Decisions: []*modelscapi.GetDecisionsStreamResponseNewItemDecisionsItems0{
 								{
-									Value:    types.StrPtr("1.2.3.5"),
-									Duration: types.StrPtr("24h"),
+									Value:    ptr.Of("1.2.3.5"),
+									Duration: ptr.Of("24h"),
 								},
 							},
 						},
@@ -1228,29 +1230,29 @@ func TestShouldShareAlert(t *testing.T) {
 		{
 			name: "custom alert should be shared if config enables it",
 			consoleConfig: &csconfig.ConsoleConfig{
-				ShareCustomScenarios: types.BoolPtr(true),
+				ShareCustomScenarios: ptr.Of(true),
 			},
-			alert:         &models.Alert{Simulated: types.BoolPtr(false)},
+			alert:         &models.Alert{Simulated: ptr.Of(false)},
 			expectedRet:   true,
 			expectedTrust: "custom",
 		},
 		{
 			name: "custom alert should not be shared if config disables it",
 			consoleConfig: &csconfig.ConsoleConfig{
-				ShareCustomScenarios: types.BoolPtr(false),
+				ShareCustomScenarios: ptr.Of(false),
 			},
-			alert:         &models.Alert{Simulated: types.BoolPtr(false)},
+			alert:         &models.Alert{Simulated: ptr.Of(false)},
 			expectedRet:   false,
 			expectedTrust: "custom",
 		},
 		{
 			name: "manual alert should be shared if config enables it",
 			consoleConfig: &csconfig.ConsoleConfig{
-				ShareManualDecisions: types.BoolPtr(true),
+				ShareManualDecisions: ptr.Of(true),
 			},
 			alert: &models.Alert{
-				Simulated: types.BoolPtr(false),
-				Decisions: []*models.Decision{{Origin: types.StrPtr(types.CscliOrigin)}},
+				Simulated: ptr.Of(false),
+				Decisions: []*models.Decision{{Origin: ptr.Of(types.CscliOrigin)}},
 			},
 			expectedRet:   true,
 			expectedTrust: "manual",
@@ -1258,11 +1260,11 @@ func TestShouldShareAlert(t *testing.T) {
 		{
 			name: "manual alert should not be shared if config disables it",
 			consoleConfig: &csconfig.ConsoleConfig{
-				ShareManualDecisions: types.BoolPtr(false),
+				ShareManualDecisions: ptr.Of(false),
 			},
 			alert: &models.Alert{
-				Simulated: types.BoolPtr(false),
-				Decisions: []*models.Decision{{Origin: types.StrPtr(types.CscliOrigin)}},
+				Simulated: ptr.Of(false),
+				Decisions: []*models.Decision{{Origin: ptr.Of(types.CscliOrigin)}},
 			},
 			expectedRet:   false,
 			expectedTrust: "manual",
@@ -1270,11 +1272,11 @@ func TestShouldShareAlert(t *testing.T) {
 		{
 			name: "manual alert should be shared if config enables it",
 			consoleConfig: &csconfig.ConsoleConfig{
-				ShareTaintedScenarios: types.BoolPtr(true),
+				ShareTaintedScenarios: ptr.Of(true),
 			},
 			alert: &models.Alert{
-				Simulated:    types.BoolPtr(false),
-				ScenarioHash: types.StrPtr("whateverHash"),
+				Simulated:    ptr.Of(false),
+				ScenarioHash: ptr.Of("whateverHash"),
 			},
 			expectedRet:   true,
 			expectedTrust: "tainted",
@@ -1282,11 +1284,11 @@ func TestShouldShareAlert(t *testing.T) {
 		{
 			name: "manual alert should not be shared if config disables it",
 			consoleConfig: &csconfig.ConsoleConfig{
-				ShareTaintedScenarios: types.BoolPtr(false),
+				ShareTaintedScenarios: ptr.Of(false),
 			},
 			alert: &models.Alert{
-				Simulated:    types.BoolPtr(false),
-				ScenarioHash: types.StrPtr("whateverHash"),
+				Simulated:    ptr.Of(false),
+				ScenarioHash: ptr.Of("whateverHash"),
 			},
 			expectedRet:   false,
 			expectedTrust: "tainted",

+ 11 - 3
pkg/apiserver/apiserver.go

@@ -12,6 +12,8 @@ import (
 	"strings"
 	"time"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/trace"
+
 	"github.com/crowdsecurity/crowdsec/pkg/apiclient"
 	"github.com/crowdsecurity/crowdsec/pkg/apiserver/controllers"
 	v1 "github.com/crowdsecurity/crowdsec/pkg/apiserver/middlewares/v1"
@@ -87,7 +89,7 @@ func CustomRecoveryWithWriter() gin.HandlerFunc {
 					log.Warningf("client %s disconnected : %s", c.ClientIP(), err)
 					c.Abort()
 				} else {
-					filename := types.WriteStackTrace(err)
+					filename := trace.WriteStackTrace(err)
 					log.Warningf("client %s error : %s", c.ClientIP(), err)
 					log.Warningf("stacktrace written to %s, please join to your issue", filename)
 					c.AbortWithStatus(http.StatusInternalServerError)
@@ -311,7 +313,13 @@ func (s *APIServer) GetTLSConfig() (*tls.Config, error) {
 			if err != nil {
 				return nil, errors.Wrap(err, "Error opening cert file")
 			}
-			caCertPool = x509.NewCertPool()
+			caCertPool, err = x509.SystemCertPool()
+			if err != nil {
+				log.Warnf("Error loading system CA certificates: %s", err)
+			}
+			if caCertPool == nil {
+				caCertPool = x509.NewCertPool()
+			}
 			caCertPool.AppendCertsFromPEM(caCert)
 		}
 	}
@@ -325,7 +333,7 @@ func (s *APIServer) GetTLSConfig() (*tls.Config, error) {
 }
 
 func (s *APIServer) Run(apiReady chan bool) error {
-	defer types.CatchPanic("lapi/runServer")
+	defer trace.CatchPanic("lapi/runServer")
 	tlsCfg, err := s.GetTLSConfig()
 	if err != nil {
 		return errors.Wrap(err, "while creating TLS config")

+ 26 - 14
pkg/apiserver/apiserver_test.go

@@ -11,8 +11,9 @@ import (
 	"testing"
 	"time"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/version"
+
 	middlewares "github.com/crowdsecurity/crowdsec/pkg/apiserver/middlewares/v1"
-	"github.com/crowdsecurity/crowdsec/pkg/cwversion"
 	"github.com/crowdsecurity/crowdsec/pkg/models"
 	"github.com/crowdsecurity/crowdsec/pkg/types"
 	"github.com/go-openapi/strfmt"
@@ -33,16 +34,19 @@ var MachineTest = models.WatcherAuthRequest{
 	Password:  &testPassword,
 }
 
-var UserAgent = fmt.Sprintf("crowdsec-test/%s", cwversion.Version)
+var UserAgent = fmt.Sprintf("crowdsec-test/%s", version.Version)
 var emptyBody = strings.NewReader("")
 
-func LoadTestConfig() csconfig.Config {
+func LoadTestConfig(t *testing.T) csconfig.Config {
 	config := csconfig.Config{}
 	maxAge := "1h"
 	flushConfig := csconfig.FlushDBCfg{
 		MaxAge: &maxAge,
 	}
+
 	tempDir, _ := os.MkdirTemp("", "crowdsec_tests")
+	t.Cleanup(func() { os.RemoveAll(tempDir) })
+
 	dbconfig := csconfig.DatabaseCfg{
 		Type:   "sqlite",
 		DbPath: filepath.Join(tempDir, "ent"),
@@ -68,13 +72,16 @@ func LoadTestConfig() csconfig.Config {
 	return config
 }
 
-func LoadTestConfigForwardedFor() csconfig.Config {
+func LoadTestConfigForwardedFor(t *testing.T) csconfig.Config {
 	config := csconfig.Config{}
 	maxAge := "1h"
 	flushConfig := csconfig.FlushDBCfg{
 		MaxAge: &maxAge,
 	}
+
 	tempDir, _ := os.MkdirTemp("", "crowdsec_tests")
+	t.Cleanup(func() { os.RemoveAll(tempDir) })
+
 	dbconfig := csconfig.DatabaseCfg{
 		Type:   "sqlite",
 		DbPath: filepath.Join(tempDir, "ent"),
@@ -102,8 +109,8 @@ func LoadTestConfigForwardedFor() csconfig.Config {
 	return config
 }
 
-func NewAPIServer() (*APIServer, csconfig.Config, error) {
-	config := LoadTestConfig()
+func NewAPIServer(t *testing.T) (*APIServer, csconfig.Config, error) {
+	config := LoadTestConfig(t)
 	os.Remove("./ent")
 	apiServer, err := NewServer(config.API.Server)
 	if err != nil {
@@ -114,8 +121,8 @@ func NewAPIServer() (*APIServer, csconfig.Config, error) {
 	return apiServer, config, nil
 }
 
-func NewAPITest() (*gin.Engine, csconfig.Config, error) {
-	apiServer, config, err := NewAPIServer()
+func NewAPITest(t *testing.T) (*gin.Engine, csconfig.Config, error) {
+	apiServer, config, err := NewAPIServer(t)
 	if err != nil {
 		return nil, config, fmt.Errorf("unable to run local API: %s", err)
 	}
@@ -130,8 +137,8 @@ func NewAPITest() (*gin.Engine, csconfig.Config, error) {
 	return router, config, nil
 }
 
-func NewAPITestForwardedFor() (*gin.Engine, csconfig.Config, error) {
-	config := LoadTestConfigForwardedFor()
+func NewAPITestForwardedFor(t *testing.T) (*gin.Engine, csconfig.Config, error) {
+	config := LoadTestConfigForwardedFor(t)
 
 	os.Remove("./ent")
 	apiServer, err := NewServer(config.API.Server)
@@ -284,7 +291,7 @@ func CreateTestBouncer(config *csconfig.DatabaseCfg) (string, error) {
 }
 
 func TestWithWrongDBConfig(t *testing.T) {
-	config := LoadTestConfig()
+	config := LoadTestConfig(t)
 	config.API.Server.DbConfig.Type = "test"
 	apiServer, err := NewServer(config.API.Server)
 
@@ -293,7 +300,7 @@ func TestWithWrongDBConfig(t *testing.T) {
 }
 
 func TestWithWrongFlushConfig(t *testing.T) {
-	config := LoadTestConfig()
+	config := LoadTestConfig(t)
 	maxItems := -1
 	config.API.Server.DbConfig.Flush.MaxItems = &maxItems
 	apiServer, err := NewServer(config.API.Server)
@@ -303,7 +310,7 @@ func TestWithWrongFlushConfig(t *testing.T) {
 }
 
 func TestUnknownPath(t *testing.T) {
-	router, _, err := NewAPITest()
+	router, _, err := NewAPITest(t)
 	if err != nil {
 		log.Fatalf("unable to run local API: %s", err)
 	}
@@ -333,13 +340,15 @@ ListenURI              string              `yaml:"listen_uri,omitempty"` //127.0
 */
 
 func TestLoggingDebugToFileConfig(t *testing.T) {
-
 	/*declare settings*/
 	maxAge := "1h"
 	flushConfig := csconfig.FlushDBCfg{
 		MaxAge: &maxAge,
 	}
+
 	tempDir, _ := os.MkdirTemp("", "crowdsec_tests")
+	t.Cleanup(func() { os.RemoveAll(tempDir) })
+
 	dbconfig := csconfig.DatabaseCfg{
 		Type:   "sqlite",
 		DbPath: filepath.Join(tempDir, "ent"),
@@ -397,7 +406,10 @@ func TestLoggingErrorToFileConfig(t *testing.T) {
 	flushConfig := csconfig.FlushDBCfg{
 		MaxAge: &maxAge,
 	}
+
 	tempDir, _ := os.MkdirTemp("", "crowdsec_tests")
+	t.Cleanup(func() { os.RemoveAll(tempDir) })
+
 	dbconfig := csconfig.DatabaseCfg{
 		Type:   "sqlite",
 		DbPath: filepath.Join(tempDir, "ent"),

+ 1 - 1
pkg/apiserver/jwt_test.go

@@ -11,7 +11,7 @@ import (
 )
 
 func TestLogin(t *testing.T) {
-	router, config, err := NewAPITest()
+	router, config, err := NewAPITest(t)
 	if err != nil {
 		log.Fatalf("unable to run local API: %s", err)
 	}

+ 5 - 5
pkg/apiserver/machines_test.go

@@ -12,7 +12,7 @@ import (
 )
 
 func TestCreateMachine(t *testing.T) {
-	router, _, err := NewAPITest()
+	router, _, err := NewAPITest(t)
 	if err != nil {
 		log.Fatalf("unable to run local API: %s", err)
 	}
@@ -53,7 +53,7 @@ func TestCreateMachine(t *testing.T) {
 }
 
 func TestCreateMachineWithForwardedFor(t *testing.T) {
-	router, config, err := NewAPITestForwardedFor()
+	router, config, err := NewAPITestForwardedFor(t)
 	if err != nil {
 		log.Fatalf("unable to run local API: %s", err)
 	}
@@ -82,7 +82,7 @@ func TestCreateMachineWithForwardedFor(t *testing.T) {
 }
 
 func TestCreateMachineWithForwardedForNoConfig(t *testing.T) {
-	router, config, err := NewAPITest()
+	router, config, err := NewAPITest(t)
 	if err != nil {
 		log.Fatalf("unable to run local API: %s", err)
 	}
@@ -113,7 +113,7 @@ func TestCreateMachineWithForwardedForNoConfig(t *testing.T) {
 }
 
 func TestCreateMachineWithoutForwardedFor(t *testing.T) {
-	router, config, err := NewAPITestForwardedFor()
+	router, config, err := NewAPITestForwardedFor(t)
 	if err != nil {
 		log.Fatalf("unable to run local API: %s", err)
 	}
@@ -143,7 +143,7 @@ func TestCreateMachineWithoutForwardedFor(t *testing.T) {
 }
 
 func TestCreateMachineAlreadyExist(t *testing.T) {
-	router, _, err := NewAPITest()
+	router, _, err := NewAPITest(t)
 	if err != nil {
 		log.Fatalf("unable to run local API: %s", err)
 	}

+ 4 - 3
pkg/apiserver/papi.go

@@ -8,6 +8,8 @@ import (
 	"sync"
 	"time"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/trace"
+
 	"github.com/crowdsecurity/crowdsec/pkg/apiclient"
 	"github.com/crowdsecurity/crowdsec/pkg/csconfig"
 	"github.com/crowdsecurity/crowdsec/pkg/database"
@@ -217,8 +219,7 @@ func (p *Papi) PullOnce(since time.Time, sync bool) error {
 
 // PullPAPI is the long polling client for real-time decisions from PAPI
 func (p *Papi) Pull() error {
-
-	defer types.CatchPanic("lapi/PullPAPI")
+	defer trace.CatchPanic("lapi/PullPAPI")
 	p.Logger.Infof("Starting Polling API Pull")
 
 	lastTimestamp := time.Time{}
@@ -270,7 +271,7 @@ func (p *Papi) Pull() error {
 }
 
 func (p *Papi) SyncDecisions() error {
-	defer types.CatchPanic("lapi/syncDecisionsToCAPI")
+	defer trace.CatchPanic("lapi/syncDecisionsToCAPI")
 
 	var cache models.DecisionsDeleteRequest
 	ticker := time.NewTicker(p.SyncInterval)

+ 16 - 14
pkg/apiserver/papi_cmd.go

@@ -5,11 +5,13 @@ import (
 	"fmt"
 	"time"
 
+	log "github.com/sirupsen/logrus"
+
+	"github.com/crowdsecurity/go-cs-lib/pkg/ptr"
+
 	"github.com/crowdsecurity/crowdsec/pkg/apiclient"
 	"github.com/crowdsecurity/crowdsec/pkg/models"
 	"github.com/crowdsecurity/crowdsec/pkg/types"
-	"github.com/pkg/errors"
-	log "github.com/sirupsen/logrus"
 )
 
 type deleteDecisions struct {
@@ -75,7 +77,7 @@ func AlertCmd(message *Message, p *Papi, sync bool) error {
 		alert := &models.Alert{}
 
 		if err := json.Unmarshal(data, alert); err != nil {
-			return errors.Wrapf(err, "message for '%s' contains bad alert format", message.Header.OperationType)
+			return fmt.Errorf("message for '%s' contains bad alert format: %w", message.Header.OperationType, err)
 		}
 
 		log.Infof("Received order %s from PAPI (%d decisions)", alert.UUID, len(alert.Decisions))
@@ -83,20 +85,20 @@ func AlertCmd(message *Message, p *Papi, sync bool) error {
 		/*Fix the alert with missing mandatory items*/
 		if alert.StartAt == nil || *alert.StartAt == "" {
 			log.Warnf("Alert %d has no StartAt, setting it to now", alert.ID)
-			alert.StartAt = types.StrPtr(time.Now().UTC().Format(time.RFC3339))
+			alert.StartAt = ptr.Of(time.Now().UTC().Format(time.RFC3339))
 		}
 		if alert.StopAt == nil || *alert.StopAt == "" {
 			log.Warnf("Alert %d has no StopAt, setting it to now", alert.ID)
-			alert.StopAt = types.StrPtr(time.Now().UTC().Format(time.RFC3339))
+			alert.StopAt = ptr.Of(time.Now().UTC().Format(time.RFC3339))
 		}
-		alert.EventsCount = types.Int32Ptr(0)
-		alert.Capacity = types.Int32Ptr(0)
-		alert.Leakspeed = types.StrPtr("")
-		alert.Simulated = types.BoolPtr(false)
-		alert.ScenarioHash = types.StrPtr("")
-		alert.ScenarioVersion = types.StrPtr("")
-		alert.Message = types.StrPtr("")
-		alert.Scenario = types.StrPtr("")
+		alert.EventsCount = ptr.Of(int32(0))
+		alert.Capacity = ptr.Of(int32(0))
+		alert.Leakspeed = ptr.Of("")
+		alert.Simulated = ptr.Of(false)
+		alert.ScenarioHash = ptr.Of("")
+		alert.ScenarioVersion = ptr.Of("")
+		alert.Message = ptr.Of("")
+		alert.Scenario = ptr.Of("")
 		alert.Source = &models.Source{}
 
 		//if we're setting Source.Scope to types.ConsoleOrigin, it messes up the alert's value
@@ -105,7 +107,7 @@ func AlertCmd(message *Message, p *Papi, sync bool) error {
 			alert.Source.Value = alert.Decisions[0].Value
 		} else {
 			log.Warningf("No decision found in alert for Polling API (%s : %s)", message.Header.Source.User, message.Header.Message)
-			alert.Source.Scope = types.StrPtr(types.ConsoleOrigin)
+			alert.Source.Scope = ptr.Of(types.ConsoleOrigin)
 			alert.Source.Value = &message.Header.Source.User
 		}
 		alert.Scenario = &message.Header.Message

+ 53 - 45
pkg/csconfig/api.go

@@ -9,13 +9,13 @@ import (
 	"strings"
 	"time"
 
-	"github.com/pkg/errors"
 	log "github.com/sirupsen/logrus"
 	"gopkg.in/yaml.v2"
 
+	"github.com/crowdsecurity/go-cs-lib/pkg/ptr"
+	"github.com/crowdsecurity/go-cs-lib/pkg/yamlpatch"
+
 	"github.com/crowdsecurity/crowdsec/pkg/apiclient"
-	"github.com/crowdsecurity/crowdsec/pkg/types"
-	"github.com/crowdsecurity/crowdsec/pkg/yamlpatch"
 )
 
 type APICfg struct {
@@ -82,11 +82,11 @@ func (o *OnlineApiClientCfg) Load() error {
 	o.Credentials = new(ApiCredentialsCfg)
 	fcontent, err := os.ReadFile(o.CredentialsFilePath)
 	if err != nil {
-		return errors.Wrapf(err, "failed to read api server credentials configuration file '%s'", o.CredentialsFilePath)
+		return fmt.Errorf("failed to read api server credentials configuration file '%s': %w", o.CredentialsFilePath, err)
 	}
 	err = yaml.UnmarshalStrict(fcontent, o.Credentials)
 	if err != nil {
-		return errors.Wrapf(err, "failed unmarshaling api server credentials configuration file '%s'", o.CredentialsFilePath)
+		return fmt.Errorf("failed unmarshaling api server credentials configuration file '%s': %w", o.CredentialsFilePath, err)
 	}
 	if o.Credentials.Login == "" || o.Credentials.Password == "" || o.Credentials.URL == "" {
 		log.Warningf("can't load CAPI credentials from '%s' (missing field)", o.CredentialsFilePath)
@@ -104,7 +104,7 @@ func (l *LocalApiClientCfg) Load() error {
 	}
 	err = yaml.UnmarshalStrict(fcontent, &l.Credentials)
 	if err != nil {
-		return errors.Wrapf(err, "failed unmarshaling api client credential configuration file '%s'", l.CredentialsFilePath)
+		return fmt.Errorf("failed unmarshaling api client credential configuration file '%s': %w", l.CredentialsFilePath, err)
 	}
 	if l.Credentials == nil || l.Credentials.URL == "" {
 		return fmt.Errorf("no credentials or URL found in api client configuration '%s'", l.CredentialsFilePath)
@@ -129,10 +129,16 @@ func (l *LocalApiClientCfg) Load() error {
 	if l.Credentials.CACertPath != "" {
 		caCert, err := os.ReadFile(l.Credentials.CACertPath)
 		if err != nil {
-			return errors.Wrapf(err, "failed to load cacert")
+			return fmt.Errorf("failed to load cacert: %w", err)
 		}
 
-		caCertPool := x509.NewCertPool()
+		caCertPool, err := x509.SystemCertPool()
+		if err != nil {
+			log.Warningf("Error loading system CA certificates: %s", err)
+		}
+		if caCertPool == nil {
+			caCertPool = x509.NewCertPool()
+		}
 		caCertPool.AppendCertsFromPEM(caCert)
 		apiclient.CaCertPool = caCertPool
 	}
@@ -140,7 +146,7 @@ func (l *LocalApiClientCfg) Load() error {
 	if l.Credentials.CertPath != "" && l.Credentials.KeyPath != "" {
 		cert, err := tls.LoadX509KeyPair(l.Credentials.CertPath, l.Credentials.KeyPath)
 		if err != nil {
-			return errors.Wrapf(err, "failed to load api client certificate")
+			return fmt.Errorf("failed to load api client certificate: %w", err)
 		}
 
 		apiclient.Cert = &cert
@@ -222,47 +228,49 @@ func (c *Config) LoadAPIServer() error {
 		log.Warning("crowdsec local API is disabled from flag")
 	}
 
-	if c.API.Server != nil {
+	if c.API.Server == nil {
+		log.Warning("crowdsec local API is disabled")
+		c.DisableAPI = true
+		return nil
+	}
 
-		//inherit log level from common, then api->server
-		var logLevel log.Level
-		if c.API.Server.LogLevel != nil {
-			logLevel = *c.API.Server.LogLevel
-		} else if c.Common.LogLevel != nil {
-			logLevel = *c.Common.LogLevel
-		} else {
-			logLevel = log.InfoLevel
-		}
+	//inherit log level from common, then api->server
+	var logLevel log.Level
+	if c.API.Server.LogLevel != nil {
+		logLevel = *c.API.Server.LogLevel
+	} else if c.Common.LogLevel != nil {
+		logLevel = *c.Common.LogLevel
+	} else {
+		logLevel = log.InfoLevel
+	}
 
-		if c.API.Server.PapiLogLevel == nil {
-			c.API.Server.PapiLogLevel = &logLevel
-		}
+	if c.API.Server.PapiLogLevel == nil {
+		c.API.Server.PapiLogLevel = &logLevel
+	}
 
-		if c.API.Server.OnlineClient != nil && c.API.Server.OnlineClient.CredentialsFilePath != "" {
-			if err := c.API.Server.OnlineClient.Load(); err != nil {
-				return errors.Wrap(err, "loading online client credentials")
-			}
-		}
-		if c.API.Server.OnlineClient == nil || c.API.Server.OnlineClient.Credentials == nil {
-			log.Printf("push and pull to Central API disabled")
-		}
-		if err := c.LoadDBConfig(); err != nil {
-			return err
+	if c.API.Server.OnlineClient != nil && c.API.Server.OnlineClient.CredentialsFilePath != "" {
+		if err := c.API.Server.OnlineClient.Load(); err != nil {
+			return fmt.Errorf("loading online client credentials: %w", err)
 		}
+	}
+	if c.API.Server.OnlineClient == nil || c.API.Server.OnlineClient.Credentials == nil {
+		log.Printf("push and pull to Central API disabled")
+	}
+	if err := c.LoadDBConfig(); err != nil {
+		return err
+	}
 
-		if err := c.API.Server.LoadCapiWhitelists(); err != nil {
-			return err
-		}
+	if err := c.API.Server.LoadCapiWhitelists(); err != nil {
+		return err
+	}
 
-	} else {
-		log.Warning("crowdsec local API is disabled")
-		c.DisableAPI = true
-		return nil
+	if c.API.Server.CapiWhitelistsPath != "" {
+		log.Infof("loaded capi whitelist from %s: %d IPs, %d CIDRs", c.API.Server.CapiWhitelistsPath, len(c.API.Server.CapiWhitelists.Ips), len(c.API.Server.CapiWhitelists.Cidrs))
 	}
 
 	if c.API.Server.Enable == nil {
 		// if the option is not present, it is enabled by default
-		c.API.Server.Enable = types.BoolPtr(true)
+		c.API.Server.Enable = ptr.Of(true)
 	}
 
 	if !*c.API.Server.Enable {
@@ -291,18 +299,18 @@ func (c *Config) LoadAPIServer() error {
 		c.API.Server.UseForwardedForHeaders = true
 	}
 	if err := c.API.Server.LoadProfiles(); err != nil {
-		return errors.Wrap(err, "while loading profiles for LAPI")
+		return fmt.Errorf("while loading profiles for LAPI: %w", err)
 	}
 	if c.API.Server.ConsoleConfigPath == "" {
 		c.API.Server.ConsoleConfigPath = DefaultConsoleConfigFilePath
 	}
 	if err := c.API.Server.LoadConsoleConfig(); err != nil {
-		return errors.Wrap(err, "while loading console options")
+		return fmt.Errorf("while loading console options: %w", err)
 	}
 
 	if c.API.Server.OnlineClient != nil && c.API.Server.OnlineClient.CredentialsFilePath != "" {
 		if err := c.API.Server.OnlineClient.Load(); err != nil {
-			return errors.Wrap(err, "loading online client credentials")
+			return fmt.Errorf("loading online client credentials: %w", err)
 		}
 	}
 	if c.API.Server.OnlineClient == nil || c.API.Server.OnlineClient.Credentials == nil {
@@ -311,7 +319,7 @@ func (c *Config) LoadAPIServer() error {
 
 	if c.API.CTI != nil {
 		if err := c.API.CTI.Load(); err != nil {
-			return errors.Wrap(err, "loading CTI configuration")
+			return fmt.Errorf("loading CTI configuration: %w", err)
 		}
 	}
 
@@ -354,7 +362,7 @@ func (s *LocalApiServerCfg) LoadCapiWhitelists() error {
 	for _, v := range fromCfg.Cidrs {
 		_, tnet, err := net.ParseCIDR(v)
 		if err != nil {
-			return fmt.Errorf("unable to parse cidr whitelist '%s' : %v.", v, err)
+			return fmt.Errorf("unable to parse cidr whitelist '%s' : %v", v, err)
 		}
 		s.CapiWhitelists.Cidrs = append(s.CapiWhitelists.Cidrs, tnet)
 	}

+ 10 - 10
pkg/csconfig/api_test.go

@@ -7,12 +7,12 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/crowdsecurity/crowdsec/pkg/types"
 	log "github.com/sirupsen/logrus"
 	"github.com/stretchr/testify/assert"
 	"gopkg.in/yaml.v2"
 
-	"github.com/crowdsecurity/crowdsec/pkg/cstest"
+	"github.com/crowdsecurity/go-cs-lib/pkg/cstest"
+	"github.com/crowdsecurity/go-cs-lib/pkg/ptr"
 )
 
 func TestLoadLocalApiClientCfg(t *testing.T) {
@@ -53,7 +53,7 @@ func TestLoadLocalApiClientCfg(t *testing.T) {
 			name: "valid configuration with insecure skip verify",
 			input: &LocalApiClientCfg{
 				CredentialsFilePath: "./tests/lapi-secrets.yaml",
-				InsecureSkipVerify:  types.BoolPtr(false),
+				InsecureSkipVerify:  ptr.Of(false),
 			},
 			expected: &ApiCredentialsCfg{
 				URL:      "http://localhost:8080/",
@@ -188,21 +188,21 @@ func TestLoadAPIServer(t *testing.T) {
 				DisableAPI: false,
 			},
 			expected: &LocalApiServerCfg{
-				Enable:    types.BoolPtr(true),
+				Enable:    ptr.Of(true),
 				ListenURI: "http://crowdsec.api",
 				TLS:       nil,
 				DbConfig: &DatabaseCfg{
 					DbPath:       "./tests/test.db",
 					Type:         "sqlite",
-					MaxOpenConns: types.IntPtr(DEFAULT_MAX_OPEN_CONNS),
+					MaxOpenConns: ptr.Of(DEFAULT_MAX_OPEN_CONNS),
 				},
 				ConsoleConfigPath: DefaultConfigPath("console.yaml"),
 				ConsoleConfig: &ConsoleConfig{
-					ShareManualDecisions:  types.BoolPtr(false),
-					ShareTaintedScenarios: types.BoolPtr(true),
-					ShareCustomScenarios:  types.BoolPtr(true),
-					ShareContext:          types.BoolPtr(false),
-					ConsoleManagement:     types.BoolPtr(false),
+					ShareManualDecisions:  ptr.Of(false),
+					ShareTaintedScenarios: ptr.Of(true),
+					ShareCustomScenarios:  ptr.Of(true),
+					ShareContext:          ptr.Of(false),
+					ConsoleManagement:     ptr.Of(false),
 				},
 				LogDir:   LogDirFullPath,
 				LogMedia: "stdout",

+ 10 - 11
pkg/csconfig/config.go

@@ -5,13 +5,12 @@ import (
 	"os"
 	"path/filepath"
 
-	"github.com/pkg/errors"
 	log "github.com/sirupsen/logrus"
 	"gopkg.in/yaml.v2"
 
-	"github.com/crowdsecurity/crowdsec/pkg/csstring"
-	"github.com/crowdsecurity/crowdsec/pkg/types"
-	"github.com/crowdsecurity/crowdsec/pkg/yamlpatch"
+	"github.com/crowdsecurity/go-cs-lib/pkg/csstring"
+	"github.com/crowdsecurity/go-cs-lib/pkg/ptr"
+	"github.com/crowdsecurity/go-cs-lib/pkg/yamlpatch"
 )
 
 // defaultConfigDir is the base path to all configuration files, to be overridden in the Makefile */
@@ -41,18 +40,18 @@ type Config struct {
 func (c *Config) Dump() error {
 	out, err := yaml.Marshal(c)
 	if err != nil {
-		return errors.Wrap(err, "failed marshaling config")
+		return fmt.Errorf("failed marshaling config: %w", err)
 	}
 	fmt.Printf("%s", string(out))
 	return nil
 }
 
-func NewConfig(configFile string, disableAgent bool, disableAPI bool, quiet bool) (*Config, error) {
+func NewConfig(configFile string, disableAgent bool, disableAPI bool, quiet bool) (*Config, string, error) {
 	patcher := yamlpatch.NewPatcher(configFile, ".local")
 	patcher.SetQuiet(quiet)
 	fcontent, err := patcher.MergedPatchContent()
 	if err != nil {
-		return nil, err
+		return nil, "", err
 	}
 	configData := csstring.StrictExpand(string(fcontent), os.LookupEnv)
 	cfg := Config{
@@ -64,9 +63,9 @@ func NewConfig(configFile string, disableAgent bool, disableAPI bool, quiet bool
 	err = yaml.UnmarshalStrict([]byte(configData), &cfg)
 	if err != nil {
 		// this is actually the "merged" yaml
-		return nil, errors.Wrap(err, configFile)
+		return nil, "", fmt.Errorf("%s: %w", configFile, err)
 	}
-	return &cfg, nil
+	return &cfg, configData, nil
 }
 
 func NewDefaultConfig() *Config {
@@ -112,14 +111,14 @@ func NewDefaultConfig() *Config {
 			},
 		},
 		CTI: &CTICfg{
-			Enabled: types.BoolPtr(false),
+			Enabled: ptr.Of(false),
 		},
 	}
 
 	dbConfig := DatabaseCfg{
 		Type:         "sqlite",
 		DbPath:       DefaultDataPath("crowdsec.db"),
-		MaxOpenConns: types.IntPtr(DEFAULT_MAX_OPEN_CONNS),
+		MaxOpenConns: ptr.Of(DEFAULT_MAX_OPEN_CONNS),
 	}
 
 	globalCfg := Config{

+ 4 - 4
pkg/csconfig/config_test.go

@@ -6,17 +6,17 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
-	"github.com/crowdsecurity/crowdsec/pkg/cstest"
+	"github.com/crowdsecurity/go-cs-lib/pkg/cstest"
 )
 
 func TestNormalLoad(t *testing.T) {
-	_, err := NewConfig("./tests/config.yaml", false, false, false)
+	_, _, err := NewConfig("./tests/config.yaml", false, false, false)
 	require.NoError(t, err)
 
-	_, err = NewConfig("./tests/xxx.yaml", false, false, false)
+	_, _, err = NewConfig("./tests/xxx.yaml", false, false, false)
 	assert.EqualError(t, err, "while reading yaml file: open ./tests/xxx.yaml: "+cstest.FileNotFoundMessage)
 
-	_, err = NewConfig("./tests/simulation.yaml", false, false, false)
+	_, _, err = NewConfig("./tests/simulation.yaml", false, false, false)
 	assert.EqualError(t, err, "./tests/simulation.yaml: yaml: unmarshal errors:\n  line 1: field simulation not found in type csconfig.Config")
 }
 

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov