Merge branch 'master' into appsec-properly-populate-event
This commit is contained in:
commit
f6038feabe
2
.github/workflows/bats-hub.yml
vendored
2
.github/workflows/bats-hub.yml
vendored
|
@ -33,7 +33,7 @@ jobs:
|
||||||
- name: "Set up Go"
|
- name: "Set up Go"
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: "1.21.8"
|
go-version: "1.21.9"
|
||||||
|
|
||||||
- name: "Install bats dependencies"
|
- name: "Install bats dependencies"
|
||||||
env:
|
env:
|
||||||
|
|
2
.github/workflows/bats-mysql.yml
vendored
2
.github/workflows/bats-mysql.yml
vendored
|
@ -36,7 +36,7 @@ jobs:
|
||||||
- name: "Set up Go"
|
- name: "Set up Go"
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: "1.21.8"
|
go-version: "1.21.9"
|
||||||
|
|
||||||
- name: "Install bats dependencies"
|
- name: "Install bats dependencies"
|
||||||
env:
|
env:
|
||||||
|
|
2
.github/workflows/bats-postgres.yml
vendored
2
.github/workflows/bats-postgres.yml
vendored
|
@ -45,7 +45,7 @@ jobs:
|
||||||
- name: "Set up Go"
|
- name: "Set up Go"
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: "1.21.8"
|
go-version: "1.21.9"
|
||||||
|
|
||||||
- name: "Install bats dependencies"
|
- name: "Install bats dependencies"
|
||||||
env:
|
env:
|
||||||
|
|
2
.github/workflows/bats-sqlite-coverage.yml
vendored
2
.github/workflows/bats-sqlite-coverage.yml
vendored
|
@ -28,7 +28,7 @@ jobs:
|
||||||
- name: "Set up Go"
|
- name: "Set up Go"
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: "1.21.8"
|
go-version: "1.21.9"
|
||||||
|
|
||||||
- name: "Install bats dependencies"
|
- name: "Install bats dependencies"
|
||||||
env:
|
env:
|
||||||
|
|
2
.github/workflows/ci-windows-build-msi.yml
vendored
2
.github/workflows/ci-windows-build-msi.yml
vendored
|
@ -35,7 +35,7 @@ jobs:
|
||||||
- name: "Set up Go"
|
- name: "Set up Go"
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: "1.21.8"
|
go-version: "1.21.9"
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: make windows_installer BUILD_RE2_WASM=1
|
run: make windows_installer BUILD_RE2_WASM=1
|
||||||
|
|
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
|
@ -52,7 +52,7 @@ jobs:
|
||||||
- name: "Set up Go"
|
- name: "Set up Go"
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: "1.21.8"
|
go-version: "1.21.9"
|
||||||
cache-dependency-path: "**/go.sum"
|
cache-dependency-path: "**/go.sum"
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
|
|
4
.github/workflows/go-tests-windows.yml
vendored
4
.github/workflows/go-tests-windows.yml
vendored
|
@ -34,7 +34,7 @@ jobs:
|
||||||
- name: "Set up Go"
|
- name: "Set up Go"
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: "1.21.8"
|
go-version: "1.21.9"
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
|
@ -56,7 +56,7 @@ jobs:
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v4
|
uses: golangci/golangci-lint-action@v4
|
||||||
with:
|
with:
|
||||||
version: v1.56
|
version: v1.57
|
||||||
args: --issues-exit-code=1 --timeout 10m
|
args: --issues-exit-code=1 --timeout 10m
|
||||||
only-new-issues: false
|
only-new-issues: false
|
||||||
# the cache is already managed above, enabling it here
|
# the cache is already managed above, enabling it here
|
||||||
|
|
4
.github/workflows/go-tests.yml
vendored
4
.github/workflows/go-tests.yml
vendored
|
@ -126,7 +126,7 @@ jobs:
|
||||||
- name: "Set up Go"
|
- name: "Set up Go"
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: "1.21.8"
|
go-version: "1.21.9"
|
||||||
|
|
||||||
- name: Create localstack streams
|
- name: Create localstack streams
|
||||||
run: |
|
run: |
|
||||||
|
@ -157,7 +157,7 @@ jobs:
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v4
|
uses: golangci/golangci-lint-action@v4
|
||||||
with:
|
with:
|
||||||
version: v1.56
|
version: v1.57
|
||||||
args: --issues-exit-code=1 --timeout 10m
|
args: --issues-exit-code=1 --timeout 10m
|
||||||
only-new-issues: false
|
only-new-issues: false
|
||||||
# the cache is already managed above, enabling it here
|
# the cache is already managed above, enabling it here
|
||||||
|
|
|
@ -25,7 +25,7 @@ jobs:
|
||||||
- name: "Set up Go"
|
- name: "Set up Go"
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: "1.21.8"
|
go-version: "1.21.9"
|
||||||
|
|
||||||
- name: Build the binaries
|
- name: Build the binaries
|
||||||
run: |
|
run: |
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
# https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml
|
# https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml
|
||||||
|
|
||||||
run:
|
|
||||||
skip-dirs:
|
|
||||||
- pkg/time/rate
|
|
||||||
skip-files:
|
|
||||||
- pkg/yamlpatch/merge.go
|
|
||||||
- pkg/yamlpatch/merge_test.go
|
|
||||||
|
|
||||||
linters-settings:
|
linters-settings:
|
||||||
cyclop:
|
cyclop:
|
||||||
# lower this after refactoring
|
# lower this after refactoring
|
||||||
|
@ -19,6 +12,10 @@ linters-settings:
|
||||||
- prefix(github.com/crowdsecurity)
|
- prefix(github.com/crowdsecurity)
|
||||||
- prefix(github.com/crowdsecurity/crowdsec)
|
- prefix(github.com/crowdsecurity/crowdsec)
|
||||||
|
|
||||||
|
gomoddirectives:
|
||||||
|
replace-allow-list:
|
||||||
|
- golang.org/x/time/rate
|
||||||
|
|
||||||
gocognit:
|
gocognit:
|
||||||
# lower this after refactoring
|
# lower this after refactoring
|
||||||
min-complexity: 145
|
min-complexity: 145
|
||||||
|
@ -40,7 +37,6 @@ linters-settings:
|
||||||
statements: 122
|
statements: 122
|
||||||
|
|
||||||
govet:
|
govet:
|
||||||
check-shadowing: true
|
|
||||||
enable:
|
enable:
|
||||||
- atomicalign
|
- atomicalign
|
||||||
- deepequalerrors
|
- deepequalerrors
|
||||||
|
@ -295,15 +291,21 @@ issues:
|
||||||
# “Look, that’s why there’s rules, understand? So that you think before you
|
# “Look, that’s why there’s rules, understand? So that you think before you
|
||||||
# break ‘em.” ― Terry Pratchett
|
# break ‘em.” ― Terry Pratchett
|
||||||
|
|
||||||
|
exclude-dirs:
|
||||||
|
- pkg/time/rate
|
||||||
|
|
||||||
|
exclude-files:
|
||||||
|
- pkg/yamlpatch/merge.go
|
||||||
|
- pkg/yamlpatch/merge_test.go
|
||||||
|
|
||||||
|
exclude-generated-strict: true
|
||||||
|
|
||||||
max-issues-per-linter: 0
|
max-issues-per-linter: 0
|
||||||
max-same-issues: 0
|
max-same-issues: 0
|
||||||
exclude-rules:
|
exclude-rules:
|
||||||
|
|
||||||
# Won't fix:
|
# Won't fix:
|
||||||
|
|
||||||
- path: go.mod
|
|
||||||
text: "replacement are not allowed: golang.org/x/time/rate"
|
|
||||||
|
|
||||||
# `err` is often shadowed, we may continue to do it
|
# `err` is often shadowed, we may continue to do it
|
||||||
- linters:
|
- linters:
|
||||||
- govet
|
- govet
|
||||||
|
|
14
Dockerfile
14
Dockerfile
|
@ -1,5 +1,5 @@
|
||||||
# vim: set ft=dockerfile:
|
# vim: set ft=dockerfile:
|
||||||
FROM golang:1.21.8-alpine3.18 AS build
|
FROM golang:1.21.9-alpine3.18 AS build
|
||||||
|
|
||||||
ARG BUILD_VERSION
|
ARG BUILD_VERSION
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ RUN make clean release DOCKER_BUILD=1 BUILD_STATIC=1 && \
|
||||||
./wizard.sh --docker-mode && \
|
./wizard.sh --docker-mode && \
|
||||||
cd - >/dev/null && \
|
cd - >/dev/null && \
|
||||||
cscli hub update && \
|
cscli hub update && \
|
||||||
|
./docker/preload-hub-items && \
|
||||||
cscli collections install crowdsecurity/linux && \
|
cscli collections install crowdsecurity/linux && \
|
||||||
cscli parsers install crowdsecurity/whitelists
|
cscli parsers install crowdsecurity/whitelists
|
||||||
|
|
||||||
|
@ -43,11 +44,12 @@ COPY --from=build /go/bin/yq /usr/local/bin/crowdsec /usr/local/bin/cscli /usr/l
|
||||||
COPY --from=build /etc/crowdsec /staging/etc/crowdsec
|
COPY --from=build /etc/crowdsec /staging/etc/crowdsec
|
||||||
COPY --from=build /go/src/crowdsec/docker/docker_start.sh /
|
COPY --from=build /go/src/crowdsec/docker/docker_start.sh /
|
||||||
COPY --from=build /go/src/crowdsec/docker/config.yaml /staging/etc/crowdsec/config.yaml
|
COPY --from=build /go/src/crowdsec/docker/config.yaml /staging/etc/crowdsec/config.yaml
|
||||||
|
COPY --from=build /var/lib/crowdsec /staging/var/lib/crowdsec
|
||||||
RUN yq -n '.url="http://0.0.0.0:8080"' | install -m 0600 /dev/stdin /staging/etc/crowdsec/local_api_credentials.yaml
|
RUN yq -n '.url="http://0.0.0.0:8080"' | install -m 0600 /dev/stdin /staging/etc/crowdsec/local_api_credentials.yaml
|
||||||
|
|
||||||
ENTRYPOINT /bin/bash /docker_start.sh
|
ENTRYPOINT /bin/bash /docker_start.sh
|
||||||
|
|
||||||
FROM slim as plugins
|
FROM slim as full
|
||||||
|
|
||||||
# Due to the wizard using cp -n, we have to copy the config files directly from the source as -n does not exist in busybox cp
|
# Due to the wizard using cp -n, we have to copy the config files directly from the source as -n does not exist in busybox cp
|
||||||
# The files are here for reference, as users will need to mount a new version to be actually able to use notifications
|
# The files are here for reference, as users will need to mount a new version to be actually able to use notifications
|
||||||
|
@ -60,11 +62,3 @@ COPY --from=build \
|
||||||
/staging/etc/crowdsec/notifications/
|
/staging/etc/crowdsec/notifications/
|
||||||
|
|
||||||
COPY --from=build /usr/local/lib/crowdsec/plugins /usr/local/lib/crowdsec/plugins
|
COPY --from=build /usr/local/lib/crowdsec/plugins /usr/local/lib/crowdsec/plugins
|
||||||
|
|
||||||
FROM slim as geoip
|
|
||||||
|
|
||||||
COPY --from=build /var/lib/crowdsec /staging/var/lib/crowdsec
|
|
||||||
|
|
||||||
FROM plugins as full
|
|
||||||
|
|
||||||
COPY --from=build /var/lib/crowdsec /staging/var/lib/crowdsec
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# vim: set ft=dockerfile:
|
# vim: set ft=dockerfile:
|
||||||
FROM golang:1.21.8-bookworm AS build
|
FROM golang:1.21.9-bookworm AS build
|
||||||
|
|
||||||
ARG BUILD_VERSION
|
ARG BUILD_VERSION
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ RUN make clean release DOCKER_BUILD=1 BUILD_STATIC=1 && \
|
||||||
./wizard.sh --docker-mode && \
|
./wizard.sh --docker-mode && \
|
||||||
cd - >/dev/null && \
|
cd - >/dev/null && \
|
||||||
cscli hub update && \
|
cscli hub update && \
|
||||||
|
./docker/preload-hub-items && \
|
||||||
cscli collections install crowdsecurity/linux && \
|
cscli collections install crowdsecurity/linux && \
|
||||||
cscli parsers install crowdsecurity/whitelists
|
cscli parsers install crowdsecurity/whitelists
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ stages:
|
||||||
- task: GoTool@0
|
- task: GoTool@0
|
||||||
displayName: "Install Go"
|
displayName: "Install Go"
|
||||||
inputs:
|
inputs:
|
||||||
version: '1.21.8'
|
version: '1.21.9'
|
||||||
|
|
||||||
- pwsh: |
|
- pwsh: |
|
||||||
choco install -y make
|
choco install -y make
|
||||||
|
|
|
@ -134,7 +134,6 @@ labels:
|
||||||
type: apache2
|
type: apache2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Recommended configuration
|
## Recommended configuration
|
||||||
|
|
||||||
### Volumes
|
### Volumes
|
||||||
|
@ -146,6 +145,14 @@ to avoid losing credentials and decision data in case of container destruction a
|
||||||
* Acquisition: `/etc/crowdsec/acquis.d` and/or `/etc/crowdsec.acquis.yaml` (yes, they can be nested in `/etc/crowdsec`)
|
* Acquisition: `/etc/crowdsec/acquis.d` and/or `/etc/crowdsec.acquis.yaml` (yes, they can be nested in `/etc/crowdsec`)
|
||||||
* Database when using SQLite (default): `/var/lib/crowdsec/data`
|
* Database when using SQLite (default): `/var/lib/crowdsec/data`
|
||||||
|
|
||||||
|
### Hub updates
|
||||||
|
|
||||||
|
To ensure you have the latest version of the collections, scenarios, parsers, etc., you can set the variable `DO_HUB_UPGRADE` to true.
|
||||||
|
This will perform an update/upgrade of the hub every time the container is started.
|
||||||
|
|
||||||
|
Be aware that if your container is misbehaving and caught in a restart loop, the CrowdSec hub may ban your IP for some time and your containers
|
||||||
|
will run with the version of the hub that is cached in the container's image. If you enable `DO_HUB_UPGRADE`, do it when your infrastructure is running
|
||||||
|
correctly and make sure you have some monitoring in place.
|
||||||
|
|
||||||
## Start a Crowdsec instance
|
## Start a Crowdsec instance
|
||||||
|
|
||||||
|
@ -316,7 +323,7 @@ config.yaml) each time the container is run.
|
||||||
| `BOUNCERS_ALLOWED_OU` | bouncer-ou | OU values allowed for bouncers, separated by comma |
|
| `BOUNCERS_ALLOWED_OU` | bouncer-ou | OU values allowed for bouncers, separated by comma |
|
||||||
| | | |
|
| | | |
|
||||||
| __Hub management__ | | |
|
| __Hub management__ | | |
|
||||||
| `NO_HUB_UPGRADE` | false | Skip hub update / upgrade when the container starts |
|
| `DO_HUB_UPGRADE` | false | Force hub update / upgrade when the container starts. If for some reason the container restarts too often, it may lead to a temporary ban from hub updates. |
|
||||||
| `COLLECTIONS` | | Collections to install, separated by space: `-e COLLECTIONS="crowdsecurity/linux crowdsecurity/apache2"` |
|
| `COLLECTIONS` | | Collections to install, separated by space: `-e COLLECTIONS="crowdsecurity/linux crowdsecurity/apache2"` |
|
||||||
| `PARSERS` | | Parsers to install, separated by space |
|
| `PARSERS` | | Parsers to install, separated by space |
|
||||||
| `SCENARIOS` | | Scenarios to install, separated by space |
|
| `SCENARIOS` | | Scenarios to install, separated by space |
|
||||||
|
|
|
@ -304,9 +304,8 @@ conf_set_if "$PLUGIN_DIR" '.config_paths.plugin_dir = strenv(PLUGIN_DIR)'
|
||||||
|
|
||||||
## Install hub items
|
## Install hub items
|
||||||
|
|
||||||
cscli hub update || true
|
if istrue "$DO_HUB_UPGRADE"; then
|
||||||
|
cscli hub update || true
|
||||||
if isfalse "$NO_HUB_UPGRADE"; then
|
|
||||||
cscli hub upgrade || true
|
cscli hub upgrade || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
22
docker/preload-hub-items
Executable file
22
docker/preload-hub-items
Executable file
|
@ -0,0 +1,22 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
# pre-download everything but don't install anything
|
||||||
|
|
||||||
|
echo "Pre-downloading Hub content..."
|
||||||
|
|
||||||
|
types=$(cscli hub types -o raw)
|
||||||
|
|
||||||
|
for itemtype in $types; do
|
||||||
|
ALL_ITEMS=$(cscli "$itemtype" list -a -o json | itemtype="$itemtype" yq '.[env(itemtype)][] | .name')
|
||||||
|
if [[ -n "${ALL_ITEMS}" ]]; then
|
||||||
|
#shellcheck disable=SC2086
|
||||||
|
cscli "$itemtype" install \
|
||||||
|
$ALL_ITEMS \
|
||||||
|
--download-only \
|
||||||
|
--error
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo " done."
|
|
@ -6,7 +6,7 @@ CROWDSEC_TEST_VERSION="dev"
|
||||||
# All of the following flavors will be tested when using the "flavor" fixture
|
# All of the following flavors will be tested when using the "flavor" fixture
|
||||||
CROWDSEC_TEST_FLAVORS="full"
|
CROWDSEC_TEST_FLAVORS="full"
|
||||||
# CROWDSEC_TEST_FLAVORS="full,slim,debian"
|
# CROWDSEC_TEST_FLAVORS="full,slim,debian"
|
||||||
# CROWDSEC_TEST_FLAVORS="full,slim,debian,geoip,plugins-debian-slim,debian-geoip,debian-plugins"
|
# CROWDSEC_TEST_FLAVORS="full,slim,debian,debian-slim"
|
||||||
|
|
||||||
# network to use
|
# network to use
|
||||||
CROWDSEC_TEST_NETWORK="net-test"
|
CROWDSEC_TEST_NETWORK="net-test"
|
||||||
|
|
|
@ -42,7 +42,7 @@ def test_flavor_content(crowdsec, flavor):
|
||||||
x = cs.cont.exec_run(
|
x = cs.cont.exec_run(
|
||||||
'ls -1 /usr/local/lib/crowdsec/plugins/')
|
'ls -1 /usr/local/lib/crowdsec/plugins/')
|
||||||
stdout = x.output.decode()
|
stdout = x.output.decode()
|
||||||
if 'slim' in flavor or 'geoip' in flavor:
|
if 'slim' in flavor:
|
||||||
# the exact return code and full message depend
|
# the exact return code and full message depend
|
||||||
# on the 'ls' implementation (busybox vs coreutils)
|
# on the 'ls' implementation (busybox vs coreutils)
|
||||||
assert x.exit_code != 0
|
assert x.exit_code != 0
|
||||||
|
|
22
go.mod
22
go.mod
|
@ -24,6 +24,7 @@ require (
|
||||||
github.com/buger/jsonparser v1.1.1
|
github.com/buger/jsonparser v1.1.1
|
||||||
github.com/c-robinson/iplib v1.0.3
|
github.com/c-robinson/iplib v1.0.3
|
||||||
github.com/cespare/xxhash/v2 v2.2.0
|
github.com/cespare/xxhash/v2 v2.2.0
|
||||||
|
github.com/corazawaf/libinjection-go v0.1.2
|
||||||
github.com/crowdsecurity/coraza/v3 v3.0.0-20240108124027-a62b8d8e5607
|
github.com/crowdsecurity/coraza/v3 v3.0.0-20240108124027-a62b8d8e5607
|
||||||
github.com/crowdsecurity/dlog v0.0.0-20170105205344-4fb5f8204f26
|
github.com/crowdsecurity/dlog v0.0.0-20170105205344-4fb5f8204f26
|
||||||
github.com/crowdsecurity/go-cs-lib v0.0.6
|
github.com/crowdsecurity/go-cs-lib v0.0.6
|
||||||
|
@ -31,7 +32,7 @@ require (
|
||||||
github.com/crowdsecurity/machineid v1.0.2
|
github.com/crowdsecurity/machineid v1.0.2
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/dghubble/sling v1.3.0
|
github.com/dghubble/sling v1.3.0
|
||||||
github.com/docker/docker v24.0.7+incompatible
|
github.com/docker/docker v24.0.9+incompatible
|
||||||
github.com/docker/go-connections v0.4.0
|
github.com/docker/go-connections v0.4.0
|
||||||
github.com/fatih/color v1.15.0
|
github.com/fatih/color v1.15.0
|
||||||
github.com/fsnotify/fsnotify v1.6.0
|
github.com/fsnotify/fsnotify v1.6.0
|
||||||
|
@ -55,7 +56,7 @@ require (
|
||||||
github.com/hashicorp/go-version v1.2.1
|
github.com/hashicorp/go-version v1.2.1
|
||||||
github.com/hexops/gotextdiff v1.0.3
|
github.com/hexops/gotextdiff v1.0.3
|
||||||
github.com/ivanpirog/coloredcobra v1.0.1
|
github.com/ivanpirog/coloredcobra v1.0.1
|
||||||
github.com/jackc/pgx/v4 v4.14.1
|
github.com/jackc/pgx/v4 v4.18.2
|
||||||
github.com/jarcoal/httpmock v1.1.0
|
github.com/jarcoal/httpmock v1.1.0
|
||||||
github.com/jszwec/csvutil v1.5.1
|
github.com/jszwec/csvutil v1.5.1
|
||||||
github.com/lithammer/dedent v1.1.0
|
github.com/lithammer/dedent v1.1.0
|
||||||
|
@ -81,9 +82,9 @@ require (
|
||||||
github.com/umahmood/haversine v0.0.0-20151105152445-808ab04add26
|
github.com/umahmood/haversine v0.0.0-20151105152445-808ab04add26
|
||||||
github.com/wasilibs/go-re2 v1.3.0
|
github.com/wasilibs/go-re2 v1.3.0
|
||||||
github.com/xhit/go-simple-mail/v2 v2.16.0
|
github.com/xhit/go-simple-mail/v2 v2.16.0
|
||||||
golang.org/x/crypto v0.17.0
|
golang.org/x/crypto v0.22.0
|
||||||
golang.org/x/mod v0.11.0
|
golang.org/x/mod v0.11.0
|
||||||
golang.org/x/sys v0.15.0
|
golang.org/x/sys v0.19.0
|
||||||
golang.org/x/text v0.14.0
|
golang.org/x/text v0.14.0
|
||||||
google.golang.org/grpc v1.56.3
|
google.golang.org/grpc v1.56.3
|
||||||
google.golang.org/protobuf v1.33.0
|
google.golang.org/protobuf v1.33.0
|
||||||
|
@ -104,7 +105,6 @@ require (
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bytedance/sonic v1.9.1 // indirect
|
github.com/bytedance/sonic v1.9.1 // indirect
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||||
github.com/corazawaf/libinjection-go v0.1.2 // indirect
|
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
|
||||||
github.com/creack/pty v1.1.18 // indirect
|
github.com/creack/pty v1.1.18 // indirect
|
||||||
|
@ -137,12 +137,12 @@ require (
|
||||||
github.com/imdario/mergo v0.3.12 // indirect
|
github.com/imdario/mergo v0.3.12 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||||
github.com/jackc/pgconn v1.10.1 // indirect
|
github.com/jackc/pgconn v1.14.3 // indirect
|
||||||
github.com/jackc/pgio v1.0.0 // indirect
|
github.com/jackc/pgio v1.0.0 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgproto3/v2 v2.2.0 // indirect
|
github.com/jackc/pgproto3/v2 v2.3.3 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||||
github.com/jackc/pgtype v1.9.1 // indirect
|
github.com/jackc/pgtype v1.14.0 // indirect
|
||||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
@ -198,9 +198,9 @@ require (
|
||||||
github.com/zclconf/go-cty v1.8.0 // indirect
|
github.com/zclconf/go-cty v1.8.0 // indirect
|
||||||
go.mongodb.org/mongo-driver v1.9.4 // indirect
|
go.mongodb.org/mongo-driver v1.9.4 // indirect
|
||||||
golang.org/x/arch v0.3.0 // indirect
|
golang.org/x/arch v0.3.0 // indirect
|
||||||
golang.org/x/net v0.19.0 // indirect
|
golang.org/x/net v0.24.0 // indirect
|
||||||
golang.org/x/sync v0.6.0 // indirect
|
golang.org/x/sync v0.6.0 // indirect
|
||||||
golang.org/x/term v0.15.0 // indirect
|
golang.org/x/term v0.19.0 // indirect
|
||||||
golang.org/x/time v0.3.0 // indirect
|
golang.org/x/time v0.3.0 // indirect
|
||||||
golang.org/x/tools v0.8.1-0.20230428195545-5283a0178901 // indirect
|
golang.org/x/tools v0.8.1-0.20230428195545-5283a0178901 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
|
|
40
go.sum
40
go.sum
|
@ -116,8 +116,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/dghubble/sling v1.3.0/go.mod h1:XXShWaBWKzNLhu2OxikSNFrlsvowtz4kyRuXUG7oQKY=
|
||||||
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
|
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/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM=
|
github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0=
|
||||||
github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/docker v24.0.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
|
@ -368,8 +368,8 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU
|
||||||
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
||||||
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
|
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
|
||||||
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
||||||
github.com/jackc/pgconn v1.10.1 h1:DzdIHIjG1AxGwoEEqS+mGsURyjt4enSmqzACXvVzOT8=
|
github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w=
|
||||||
github.com/jackc/pgconn v1.10.1/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM=
|
||||||
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
||||||
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||||
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
||||||
|
@ -385,26 +385,26 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvW
|
||||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||||
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
github.com/jackc/pgproto3/v2 v2.2.0 h1:r7JypeP2D3onoQTCxWdTpCtJ4D+qpKr0TxvoyMhZ5ns=
|
github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag=
|
||||||
github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
|
|
||||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||||
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
||||||
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
||||||
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
|
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
|
||||||
github.com/jackc/pgtype v1.9.1 h1:MJc2s0MFS8C3ok1wQTdQxWuXQcB6+HwAm5x1CzW7mf0=
|
github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw=
|
||||||
github.com/jackc/pgtype v1.9.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
||||||
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||||
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
||||||
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||||
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
|
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
|
||||||
github.com/jackc/pgx/v4 v4.14.1 h1:71oo1KAGI6mXhLiTMn6iDFcp3e7+zon/capWjl2OEFU=
|
github.com/jackc/pgx/v4 v4.18.2 h1:xVpYkNR5pk5bMCZGfClbO962UIqVABcAGt7ha1s/FeU=
|
||||||
github.com/jackc/pgx/v4 v4.14.1/go.mod h1:RgDuE4Z34o7XE92RpLsvFiOEfrAUT0Xt2KxvX73W06M=
|
github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=
|
||||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jackc/puddle v1.2.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
|
||||||
github.com/jarcoal/httpmock v1.1.0 h1:F47ChZj1Y2zFsCXxNkBPwNNKnAyOATcdQibk0qEdVCE=
|
github.com/jarcoal/httpmock v1.1.0 h1:F47ChZj1Y2zFsCXxNkBPwNNKnAyOATcdQibk0qEdVCE=
|
||||||
github.com/jarcoal/httpmock v1.1.0/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
github.com/jarcoal/httpmock v1.1.0/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
||||||
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
|
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
|
||||||
|
@ -757,8 +757,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
@ -791,8 +791,8 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||||
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
@ -841,8 +841,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
@ -850,8 +850,8 @@ golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||||
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
|
||||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
|
714
pkg/acquisition/modules/appsec/appsec_hooks_test.go
Normal file
714
pkg/acquisition/modules/appsec/appsec_hooks_test.go
Normal file
|
@ -0,0 +1,714 @@
|
||||||
|
package appsecacquisition
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/appsec"
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/appsec/appsec_rule"
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAppsecOnMatchHooks(t *testing.T) {
|
||||||
|
tests := []appsecRuleTest{
|
||||||
|
{
|
||||||
|
name: "no rule : check return code",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Len(t, responses, 1)
|
||||||
|
require.Equal(t, 403, responses[0].BouncerHTTPResponseCode)
|
||||||
|
require.Equal(t, 403, responses[0].UserHTTPResponseCode)
|
||||||
|
require.Equal(t, appsec.BanRemediation, responses[0].Action)
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "on_match: change return code",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
on_match: []appsec.Hook{
|
||||||
|
{Filter: "IsInBand == true", Apply: []string{"SetReturnCode(413)"}},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Len(t, responses, 1)
|
||||||
|
require.Equal(t, 403, responses[0].BouncerHTTPResponseCode)
|
||||||
|
require.Equal(t, 413, responses[0].UserHTTPResponseCode)
|
||||||
|
require.Equal(t, appsec.BanRemediation, responses[0].Action)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "on_match: change action to a non standard one (log)",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
on_match: []appsec.Hook{
|
||||||
|
{Filter: "IsInBand == true", Apply: []string{"SetRemediation('log')"}},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Len(t, responses, 1)
|
||||||
|
require.Equal(t, "log", responses[0].Action)
|
||||||
|
require.Equal(t, 403, responses[0].BouncerHTTPResponseCode)
|
||||||
|
require.Equal(t, 403, responses[0].UserHTTPResponseCode)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "on_match: change action to another standard one (allow)",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
on_match: []appsec.Hook{
|
||||||
|
{Filter: "IsInBand == true", Apply: []string{"SetRemediation('allow')"}},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Len(t, responses, 1)
|
||||||
|
require.Equal(t, appsec.AllowRemediation, responses[0].Action)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "on_match: change action to another standard one (ban)",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
on_match: []appsec.Hook{
|
||||||
|
{Filter: "IsInBand == true", Apply: []string{"SetRemediation('ban')"}},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, responses, 1)
|
||||||
|
//note: SetAction normalizes deny, ban and block to ban
|
||||||
|
require.Equal(t, appsec.BanRemediation, responses[0].Action)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "on_match: change action to another standard one (captcha)",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
on_match: []appsec.Hook{
|
||||||
|
{Filter: "IsInBand == true", Apply: []string{"SetRemediation('captcha')"}},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, responses, 1)
|
||||||
|
//note: SetAction normalizes deny, ban and block to ban
|
||||||
|
require.Equal(t, appsec.CaptchaRemediation, responses[0].Action)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "on_match: change action to a non standard one",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
on_match: []appsec.Hook{
|
||||||
|
{Filter: "IsInBand == true", Apply: []string{"SetRemediation('foobar')"}},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Len(t, responses, 1)
|
||||||
|
require.Equal(t, "foobar", responses[0].Action)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "on_match: cancel alert",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule42",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
on_match: []appsec.Hook{
|
||||||
|
{Filter: "IsInBand == true && LogInfo('XX -> %s', evt.Appsec.MatchedRules.GetName())", Apply: []string{"CancelAlert()"}},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 1)
|
||||||
|
require.Equal(t, types.LOG, events[0].Type)
|
||||||
|
require.Len(t, responses, 1)
|
||||||
|
require.Equal(t, appsec.BanRemediation, responses[0].Action)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "on_match: cancel event",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule42",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
on_match: []appsec.Hook{
|
||||||
|
{Filter: "IsInBand == true", Apply: []string{"CancelEvent()"}},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 1)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Len(t, responses, 1)
|
||||||
|
require.Equal(t, appsec.BanRemediation, responses[0].Action)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
loadAppSecEngine(test, t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAppsecPreEvalHooks(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []appsecRuleTest{
|
||||||
|
{
|
||||||
|
name: "Basic on_load hook to disable inband rule",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pre_eval: []appsec.Hook{
|
||||||
|
{Filter: "1 == 1", Apply: []string{"RemoveInBandRuleByName('rule1')"}},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Empty(t, events)
|
||||||
|
require.Len(t, responses, 1)
|
||||||
|
require.False(t, responses[0].InBandInterrupt)
|
||||||
|
require.False(t, responses[0].OutOfBandInterrupt)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Basic on_load fails to disable rule",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pre_eval: []appsec.Hook{
|
||||||
|
{Filter: "1 ==2", Apply: []string{"RemoveInBandRuleByName('rule1')"}},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.True(t, events[1].Appsec.HasInBandMatches)
|
||||||
|
require.Len(t, events[1].Appsec.MatchedRules, 1)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
|
||||||
|
require.Len(t, responses, 1)
|
||||||
|
require.True(t, responses[0].InBandInterrupt)
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "on_load : disable inband by tag",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rulez",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pre_eval: []appsec.Hook{
|
||||||
|
{Apply: []string{"RemoveInBandRuleByTag('crowdsec-rulez')"}},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Empty(t, events)
|
||||||
|
require.Len(t, responses, 1)
|
||||||
|
require.False(t, responses[0].InBandInterrupt)
|
||||||
|
require.False(t, responses[0].OutOfBandInterrupt)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "on_load : disable inband by ID",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rulez",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pre_eval: []appsec.Hook{
|
||||||
|
{Apply: []string{"RemoveInBandRuleByID(1516470898)"}}, //rule ID is generated at runtime. If you change rule, it will break the test (:
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Empty(t, events)
|
||||||
|
require.Len(t, responses, 1)
|
||||||
|
require.False(t, responses[0].InBandInterrupt)
|
||||||
|
require.False(t, responses[0].OutOfBandInterrupt)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "on_load : disable inband by name",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rulez",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pre_eval: []appsec.Hook{
|
||||||
|
{Apply: []string{"RemoveInBandRuleByName('rulez')"}},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Empty(t, events)
|
||||||
|
require.Len(t, responses, 1)
|
||||||
|
require.False(t, responses[0].InBandInterrupt)
|
||||||
|
require.False(t, responses[0].OutOfBandInterrupt)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "on_load : outofband default behavior",
|
||||||
|
expected_load_ok: true,
|
||||||
|
outofband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rulez",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 1)
|
||||||
|
require.Equal(t, types.LOG, events[0].Type)
|
||||||
|
require.True(t, events[0].Appsec.HasOutBandMatches)
|
||||||
|
require.False(t, events[0].Appsec.HasInBandMatches)
|
||||||
|
require.Len(t, events[0].Appsec.MatchedRules, 1)
|
||||||
|
require.Equal(t, "rulez", events[0].Appsec.MatchedRules[0]["msg"])
|
||||||
|
//maybe surprising, but response won't mention OOB event, as it's sent as soon as the inband phase is over.
|
||||||
|
require.Len(t, responses, 1)
|
||||||
|
require.False(t, responses[0].InBandInterrupt)
|
||||||
|
require.False(t, responses[0].OutOfBandInterrupt)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "on_load : set remediation by tag",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rulez",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pre_eval: []appsec.Hook{
|
||||||
|
{Apply: []string{"SetRemediationByTag('crowdsec-rulez', 'foobar')"}}, //rule ID is generated at runtime. If you change rule, it will break the test (:
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Len(t, responses, 1)
|
||||||
|
require.Equal(t, "foobar", responses[0].Action)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "on_load : set remediation by name",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rulez",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pre_eval: []appsec.Hook{
|
||||||
|
{Apply: []string{"SetRemediationByName('rulez', 'foobar')"}}, //rule ID is generated at runtime. If you change rule, it will break the test (:
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Len(t, responses, 1)
|
||||||
|
require.Equal(t, "foobar", responses[0].Action)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "on_load : set remediation by ID",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rulez",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pre_eval: []appsec.Hook{
|
||||||
|
{Apply: []string{"SetRemediationByID(1516470898, 'foobar')"}}, //rule ID is generated at runtime. If you change rule, it will break the test (:
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Len(t, responses, 1)
|
||||||
|
require.Equal(t, "foobar", responses[0].Action)
|
||||||
|
require.Equal(t, "foobar", appsecResponse.Action)
|
||||||
|
require.Equal(t, http.StatusForbidden, appsecResponse.HTTPStatus)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
loadAppSecEngine(test, t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAppsecRemediationConfigHooks(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []appsecRuleTest{
|
||||||
|
{
|
||||||
|
name: "Basic matching rule",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Equal(t, appsec.BanRemediation, responses[0].Action)
|
||||||
|
require.Equal(t, http.StatusForbidden, statusCode)
|
||||||
|
require.Equal(t, appsec.BanRemediation, appsecResponse.Action)
|
||||||
|
require.Equal(t, http.StatusForbidden, appsecResponse.HTTPStatus)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SetRemediation",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
on_match: []appsec.Hook{{Apply: []string{"SetRemediation('captcha')"}}}, //rule ID is generated at runtime. If you change rule, it will break the test (:
|
||||||
|
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Equal(t, appsec.CaptchaRemediation, responses[0].Action)
|
||||||
|
require.Equal(t, http.StatusForbidden, statusCode)
|
||||||
|
require.Equal(t, appsec.CaptchaRemediation, appsecResponse.Action)
|
||||||
|
require.Equal(t, http.StatusForbidden, appsecResponse.HTTPStatus)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SetRemediation",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
on_match: []appsec.Hook{{Apply: []string{"SetReturnCode(418)"}}}, //rule ID is generated at runtime. If you change rule, it will break the test (:
|
||||||
|
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Equal(t, appsec.BanRemediation, responses[0].Action)
|
||||||
|
require.Equal(t, http.StatusForbidden, statusCode)
|
||||||
|
require.Equal(t, appsec.BanRemediation, appsecResponse.Action)
|
||||||
|
require.Equal(t, http.StatusTeapot, appsecResponse.HTTPStatus)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
loadAppSecEngine(test, t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestOnMatchRemediationHooks(t *testing.T) {
|
||||||
|
tests := []appsecRuleTest{
|
||||||
|
{
|
||||||
|
name: "set remediation to allow with on_match hook",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule42",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
on_match: []appsec.Hook{
|
||||||
|
{Filter: "IsInBand == true", Apply: []string{"SetRemediation('allow')"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Equal(t, appsec.AllowRemediation, appsecResponse.Action)
|
||||||
|
require.Equal(t, http.StatusOK, appsecResponse.HTTPStatus)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "set remediation to captcha + custom user code with on_match hook",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule42",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
DefaultRemediation: appsec.AllowRemediation,
|
||||||
|
on_match: []appsec.Hook{
|
||||||
|
{Filter: "IsInBand == true", Apply: []string{"SetRemediation('captcha')", "SetReturnCode(418)"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
spew.Dump(responses)
|
||||||
|
spew.Dump(appsecResponse)
|
||||||
|
|
||||||
|
log.Errorf("http status : %d", statusCode)
|
||||||
|
require.Equal(t, appsec.CaptchaRemediation, appsecResponse.Action)
|
||||||
|
require.Equal(t, http.StatusTeapot, appsecResponse.HTTPStatus)
|
||||||
|
require.Equal(t, http.StatusForbidden, statusCode)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
loadAppSecEngine(test, t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
74
pkg/acquisition/modules/appsec/appsec_lnx_test.go
Normal file
74
pkg/acquisition/modules/appsec/appsec_lnx_test.go
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
//go:build !windows
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package appsecacquisition
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/appsec"
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/appsec/appsec_rule"
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAppsecRuleTransformsOthers(t *testing.T) {
|
||||||
|
|
||||||
|
log.SetLevel(log.TraceLevel)
|
||||||
|
tests := []appsecRuleTest{
|
||||||
|
{
|
||||||
|
name: "normalizepath",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "b/c"},
|
||||||
|
Transform: []string{"normalizepath"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/?foo=a/../b/c",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "normalizepath #2",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "b/c/"},
|
||||||
|
Transform: []string{"normalizepath"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/?foo=a/../b/c/////././././",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
loadAppSecEngine(test, t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
320
pkg/acquisition/modules/appsec/appsec_remediation_test.go
Normal file
320
pkg/acquisition/modules/appsec/appsec_remediation_test.go
Normal file
|
@ -0,0 +1,320 @@
|
||||||
|
package appsecacquisition
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/appsec"
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/appsec/appsec_rule"
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAppsecDefaultPassRemediation(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []appsecRuleTest{
|
||||||
|
{
|
||||||
|
name: "Basic non-matching rule",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/",
|
||||||
|
Args: url.Values{"foo": []string{"tutu"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Equal(t, appsec.AllowRemediation, responses[0].Action)
|
||||||
|
require.Equal(t, http.StatusOK, statusCode)
|
||||||
|
require.Equal(t, appsec.AllowRemediation, appsecResponse.Action)
|
||||||
|
require.Equal(t, http.StatusOK, appsecResponse.HTTPStatus)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "DefaultPassAction: pass",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/",
|
||||||
|
Args: url.Values{"foo": []string{"tutu"}},
|
||||||
|
},
|
||||||
|
DefaultPassAction: "allow",
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Equal(t, appsec.AllowRemediation, responses[0].Action)
|
||||||
|
require.Equal(t, http.StatusOK, statusCode)
|
||||||
|
require.Equal(t, appsec.AllowRemediation, appsecResponse.Action)
|
||||||
|
require.Equal(t, http.StatusOK, appsecResponse.HTTPStatus)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "DefaultPassAction: captcha",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/",
|
||||||
|
Args: url.Values{"foo": []string{"tutu"}},
|
||||||
|
},
|
||||||
|
DefaultPassAction: "captcha",
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Equal(t, appsec.CaptchaRemediation, responses[0].Action)
|
||||||
|
require.Equal(t, http.StatusOK, statusCode) //@tko: body is captcha, but as it's 200, captcha won't be showed to user
|
||||||
|
require.Equal(t, appsec.CaptchaRemediation, appsecResponse.Action)
|
||||||
|
require.Equal(t, http.StatusOK, appsecResponse.HTTPStatus)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "DefaultPassHTTPCode: 200",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/",
|
||||||
|
Args: url.Values{"foo": []string{"tutu"}},
|
||||||
|
},
|
||||||
|
UserPassedHTTPCode: 200,
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Equal(t, appsec.AllowRemediation, responses[0].Action)
|
||||||
|
require.Equal(t, http.StatusOK, statusCode)
|
||||||
|
require.Equal(t, appsec.AllowRemediation, appsecResponse.Action)
|
||||||
|
require.Equal(t, http.StatusOK, appsecResponse.HTTPStatus)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "DefaultPassHTTPCode: 200",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/",
|
||||||
|
Args: url.Values{"foo": []string{"tutu"}},
|
||||||
|
},
|
||||||
|
UserPassedHTTPCode: 418,
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Equal(t, appsec.AllowRemediation, responses[0].Action)
|
||||||
|
require.Equal(t, http.StatusOK, statusCode)
|
||||||
|
require.Equal(t, appsec.AllowRemediation, appsecResponse.Action)
|
||||||
|
require.Equal(t, http.StatusTeapot, appsecResponse.HTTPStatus)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
loadAppSecEngine(test, t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAppsecDefaultRemediation(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []appsecRuleTest{
|
||||||
|
{
|
||||||
|
name: "Basic matching rule",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Equal(t, appsec.BanRemediation, responses[0].Action)
|
||||||
|
require.Equal(t, http.StatusForbidden, statusCode)
|
||||||
|
require.Equal(t, appsec.BanRemediation, appsecResponse.Action)
|
||||||
|
require.Equal(t, http.StatusForbidden, appsecResponse.HTTPStatus)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "default remediation to ban (default)",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule42",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
DefaultRemediation: "ban",
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Equal(t, appsec.BanRemediation, responses[0].Action)
|
||||||
|
require.Equal(t, http.StatusForbidden, statusCode)
|
||||||
|
require.Equal(t, appsec.BanRemediation, appsecResponse.Action)
|
||||||
|
require.Equal(t, http.StatusForbidden, appsecResponse.HTTPStatus)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "default remediation to allow",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule42",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
DefaultRemediation: "allow",
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Equal(t, appsec.AllowRemediation, responses[0].Action)
|
||||||
|
require.Equal(t, http.StatusOK, statusCode)
|
||||||
|
require.Equal(t, appsec.AllowRemediation, appsecResponse.Action)
|
||||||
|
require.Equal(t, http.StatusOK, appsecResponse.HTTPStatus)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "default remediation to captcha",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule42",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
DefaultRemediation: "captcha",
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Equal(t, appsec.CaptchaRemediation, responses[0].Action)
|
||||||
|
require.Equal(t, http.StatusForbidden, statusCode)
|
||||||
|
require.Equal(t, appsec.CaptchaRemediation, appsecResponse.Action)
|
||||||
|
require.Equal(t, http.StatusForbidden, appsecResponse.HTTPStatus)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom user HTTP code",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule42",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
UserBlockedHTTPCode: 418,
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Equal(t, appsec.BanRemediation, responses[0].Action)
|
||||||
|
require.Equal(t, http.StatusForbidden, statusCode)
|
||||||
|
require.Equal(t, appsec.BanRemediation, appsecResponse.Action)
|
||||||
|
require.Equal(t, http.StatusTeapot, appsecResponse.HTTPStatus)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom remediation + HTTP code",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule42",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
UserBlockedHTTPCode: 418,
|
||||||
|
DefaultRemediation: "foobar",
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Equal(t, "foobar", responses[0].Action)
|
||||||
|
require.Equal(t, http.StatusForbidden, statusCode)
|
||||||
|
require.Equal(t, "foobar", appsecResponse.Action)
|
||||||
|
require.Equal(t, http.StatusTeapot, appsecResponse.HTTPStatus)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
loadAppSecEngine(test, t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
733
pkg/acquisition/modules/appsec/appsec_rules_test.go
Normal file
733
pkg/acquisition/modules/appsec/appsec_rules_test.go
Normal file
|
@ -0,0 +1,733 @@
|
||||||
|
package appsecacquisition
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/appsec"
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/appsec/appsec_rule"
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAppsecRuleMatches(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []appsecRuleTest{
|
||||||
|
{
|
||||||
|
name: "Basic matching rule",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.True(t, events[1].Appsec.HasInBandMatches)
|
||||||
|
require.Len(t, events[1].Appsec.MatchedRules, 1)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
|
||||||
|
require.Len(t, responses, 1)
|
||||||
|
require.True(t, responses[0].InBandInterrupt)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Basic non-matching rule",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"tutu"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Empty(t, events)
|
||||||
|
require.Len(t, responses, 1)
|
||||||
|
require.False(t, responses[0].InBandInterrupt)
|
||||||
|
require.False(t, responses[0].OutOfBandInterrupt)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "default remediation to allow",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule42",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
DefaultRemediation: "allow",
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Equal(t, appsec.AllowRemediation, responses[0].Action)
|
||||||
|
require.Equal(t, http.StatusOK, statusCode)
|
||||||
|
require.Equal(t, appsec.AllowRemediation, appsecResponse.Action)
|
||||||
|
require.Equal(t, http.StatusOK, appsecResponse.HTTPStatus)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "default remediation to captcha",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule42",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
DefaultRemediation: "captcha",
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Equal(t, appsec.CaptchaRemediation, responses[0].Action)
|
||||||
|
require.Equal(t, http.StatusForbidden, statusCode)
|
||||||
|
require.Equal(t, appsec.CaptchaRemediation, appsecResponse.Action)
|
||||||
|
require.Equal(t, http.StatusForbidden, appsecResponse.HTTPStatus)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no default remediation / custom user HTTP code",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule42",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"toto"}},
|
||||||
|
},
|
||||||
|
UserBlockedHTTPCode: 418,
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Equal(t, appsec.BanRemediation, responses[0].Action)
|
||||||
|
require.Equal(t, http.StatusForbidden, statusCode)
|
||||||
|
require.Equal(t, appsec.BanRemediation, appsecResponse.Action)
|
||||||
|
require.Equal(t, http.StatusTeapot, appsecResponse.HTTPStatus)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no match but try to set remediation to captcha with on_match hook",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule42",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
on_match: []appsec.Hook{
|
||||||
|
{Filter: "IsInBand == true", Apply: []string{"SetRemediation('captcha')"}},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"bla"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Empty(t, events)
|
||||||
|
require.Equal(t, http.StatusOK, statusCode)
|
||||||
|
require.Equal(t, appsec.AllowRemediation, appsecResponse.Action)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no match but try to set user HTTP code with on_match hook",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule42",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
on_match: []appsec.Hook{
|
||||||
|
{Filter: "IsInBand == true", Apply: []string{"SetReturnCode(418)"}},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"bla"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Empty(t, events)
|
||||||
|
require.Equal(t, http.StatusOK, statusCode)
|
||||||
|
require.Equal(t, appsec.AllowRemediation, appsecResponse.Action)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no match but try to set remediation with pre_eval hook",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule42",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pre_eval: []appsec.Hook{
|
||||||
|
{Filter: "IsInBand == true", Apply: []string{"SetRemediationByName('rule42', 'captcha')"}},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/urllll",
|
||||||
|
Args: url.Values{"foo": []string{"bla"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Empty(t, events)
|
||||||
|
require.Equal(t, http.StatusOK, statusCode)
|
||||||
|
require.Equal(t, appsec.AllowRemediation, appsecResponse.Action)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
loadAppSecEngine(test, t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAppsecRuleTransforms(t *testing.T) {
|
||||||
|
|
||||||
|
log.SetLevel(log.TraceLevel)
|
||||||
|
tests := []appsecRuleTest{
|
||||||
|
{
|
||||||
|
name: "Basic matching rule",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"URI"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "/toto"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/toto",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "lowercase",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"URI"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "/toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/TOTO",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "uppercase",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"URI"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "/TOTO"},
|
||||||
|
Transform: []string{"uppercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/toto",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "b64decode",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "toto"},
|
||||||
|
Transform: []string{"b64decode"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/?foo=dG90bw",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "b64decode with extra padding",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "toto"},
|
||||||
|
Transform: []string{"b64decode"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/?foo=dG90bw===",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "length",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "gte", Value: "3"},
|
||||||
|
Transform: []string{"length"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/?foo=toto",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "urldecode",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "BB/A"},
|
||||||
|
Transform: []string{"urldecode"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/?foo=%42%42%2F%41",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "trim",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "BB/A"},
|
||||||
|
Transform: []string{"urldecode", "trim"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/?foo=%20%20%42%42%2F%41%20%20",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
loadAppSecEngine(test, t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAppsecRuleZones(t *testing.T) {
|
||||||
|
|
||||||
|
log.SetLevel(log.TraceLevel)
|
||||||
|
tests := []appsecRuleTest{
|
||||||
|
{
|
||||||
|
name: "rule: ARGS",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "toto"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "rule2",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "foobar"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/foobar?something=toto&foobar=smth",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rule: ARGS_NAMES",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS_NAMES"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "toto"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "rule2",
|
||||||
|
Zones: []string{"ARGS_NAMES"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "foobar"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/foobar?something=toto&foobar=smth",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule2", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rule: BODY_ARGS",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"BODY_ARGS"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "toto"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "rule2",
|
||||||
|
Zones: []string{"BODY_ARGS"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "foobar"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/",
|
||||||
|
Body: []byte("smth=toto&foobar=other"),
|
||||||
|
Headers: http.Header{"Content-Type": []string{"application/x-www-form-urlencoded"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rule: BODY_ARGS_NAMES",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"BODY_ARGS_NAMES"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "toto"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "rule2",
|
||||||
|
Zones: []string{"BODY_ARGS_NAMES"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "foobar"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/",
|
||||||
|
Body: []byte("smth=toto&foobar=other"),
|
||||||
|
Headers: http.Header{"Content-Type": []string{"application/x-www-form-urlencoded"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule2", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rule: HEADERS",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"HEADERS"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "toto"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "rule2",
|
||||||
|
Zones: []string{"HEADERS"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "foobar"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/",
|
||||||
|
Headers: http.Header{"foobar": []string{"toto"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rule: HEADERS_NAMES",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"HEADERS_NAMES"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "toto"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "rule2",
|
||||||
|
Zones: []string{"HEADERS_NAMES"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "foobar"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/",
|
||||||
|
Headers: http.Header{"foobar": []string{"toto"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule2", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rule: METHOD",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"METHOD"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "GET"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rule: PROTOCOL",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"PROTOCOL"},
|
||||||
|
Match: appsec_rule.Match{Type: "contains", Value: "3.1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/",
|
||||||
|
Proto: "HTTP/3.1",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rule: URI",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"URI"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "/foobar"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/foobar",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rule: URI_FULL",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"URI_FULL"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "/foobar?a=b"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/foobar?a=b",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rule: RAW_BODY",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"RAW_BODY"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "foobar=42421"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/",
|
||||||
|
Body: []byte("foobar=42421"),
|
||||||
|
Headers: http.Header{"Content-Type": []string{"application/x-www-form-urlencoded"}},
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
loadAppSecEngine(test, t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
46
pkg/acquisition/modules/appsec/appsec_win_test.go
Normal file
46
pkg/acquisition/modules/appsec/appsec_win_test.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package appsecacquisition
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAppsecRuleTransformsWindows(t *testing.T) {
|
||||||
|
|
||||||
|
log.SetLevel(log.TraceLevel)
|
||||||
|
tests := []appsecRuleTest{
|
||||||
|
// {
|
||||||
|
// name: "normalizepath",
|
||||||
|
// expected_load_ok: true,
|
||||||
|
// inband_rules: []appsec_rule.CustomRule{
|
||||||
|
// {
|
||||||
|
// Name: "rule1",
|
||||||
|
// Zones: []string{"ARGS"},
|
||||||
|
// Variables: []string{"foo"},
|
||||||
|
// Match: appsec_rule.Match{Type: "equals", Value: "b/c"},
|
||||||
|
// Transform: []string{"normalizepath"},
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// input_request: appsec.ParsedRequest{
|
||||||
|
// RemoteAddr: "1.2.3.4",
|
||||||
|
// Method: "GET",
|
||||||
|
// URI: "/?foo=a/../b/c",
|
||||||
|
// },
|
||||||
|
// output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
// require.Len(t, events, 2)
|
||||||
|
// require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
// require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
// require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
loadAppSecEngine(test, t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,7 +19,8 @@ var zonesMap map[string]string = map[string]string{
|
||||||
"HEADERS": "REQUEST_HEADERS",
|
"HEADERS": "REQUEST_HEADERS",
|
||||||
"METHOD": "REQUEST_METHOD",
|
"METHOD": "REQUEST_METHOD",
|
||||||
"PROTOCOL": "REQUEST_PROTOCOL",
|
"PROTOCOL": "REQUEST_PROTOCOL",
|
||||||
"URI": "REQUEST_URI",
|
"URI": "REQUEST_FILENAME",
|
||||||
|
"URI_FULL": "REQUEST_URI",
|
||||||
"RAW_BODY": "REQUEST_BODY",
|
"RAW_BODY": "REQUEST_BODY",
|
||||||
"FILENAMES": "FILES",
|
"FILENAMES": "FILES",
|
||||||
}
|
}
|
||||||
|
@ -28,8 +29,14 @@ var transformMap map[string]string = map[string]string{
|
||||||
"lowercase": "t:lowercase",
|
"lowercase": "t:lowercase",
|
||||||
"uppercase": "t:uppercase",
|
"uppercase": "t:uppercase",
|
||||||
"b64decode": "t:base64Decode",
|
"b64decode": "t:base64Decode",
|
||||||
"hexdecode": "t:hexDecode",
|
//"hexdecode": "t:hexDecode", -> not supported by coraza
|
||||||
"length": "t:length",
|
"length": "t:length",
|
||||||
|
"urldecode": "t:urlDecode",
|
||||||
|
"trim": "t:trim",
|
||||||
|
"normalize_path": "t:normalizePath",
|
||||||
|
"normalizepath": "t:normalizePath",
|
||||||
|
"htmlentitydecode": "t:htmlEntityDecode",
|
||||||
|
"html_entity_decode": "t:htmlEntityDecode",
|
||||||
}
|
}
|
||||||
|
|
||||||
var matchMap map[string]string = map[string]string{
|
var matchMap map[string]string = map[string]string{
|
||||||
|
|
|
@ -365,11 +365,11 @@ func NewParsedRequestFromRequest(r *http.Request, logger *logrus.Entry) (ParsedR
|
||||||
UUID: uuid.New().String(),
|
UUID: uuid.New().String(),
|
||||||
ClientHost: clientHost,
|
ClientHost: clientHost,
|
||||||
ClientIP: clientIP,
|
ClientIP: clientIP,
|
||||||
URI: parsedURL.Path,
|
URI: clientURI,
|
||||||
Method: clientMethod,
|
Method: clientMethod,
|
||||||
Host: r.Host,
|
Host: clientHost,
|
||||||
Headers: r.Header,
|
Headers: r.Header,
|
||||||
URL: r.URL,
|
URL: parsedURL,
|
||||||
Proto: r.Proto,
|
Proto: r.Proto,
|
||||||
Body: body,
|
Body: body,
|
||||||
Args: ParseQuery(parsedURL.RawQuery),
|
Args: ParseQuery(parsedURL.RawQuery),
|
||||||
|
|
|
@ -13,6 +13,7 @@ type ConfigurationPaths struct {
|
||||||
HubDir string `yaml:"hub_dir,omitempty"`
|
HubDir string `yaml:"hub_dir,omitempty"`
|
||||||
PluginDir string `yaml:"plugin_dir,omitempty"`
|
PluginDir string `yaml:"plugin_dir,omitempty"`
|
||||||
NotificationDir string `yaml:"notification_dir,omitempty"`
|
NotificationDir string `yaml:"notification_dir,omitempty"`
|
||||||
|
PatternDir string `yaml:"pattern_dir,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) loadConfigurationPaths() error {
|
func (c *Config) loadConfigurationPaths() error {
|
||||||
|
@ -33,6 +34,10 @@ func (c *Config) loadConfigurationPaths() error {
|
||||||
c.ConfigPaths.HubIndexFile = filepath.Clean(c.ConfigPaths.HubDir + "/.index.json")
|
c.ConfigPaths.HubIndexFile = filepath.Clean(c.ConfigPaths.HubDir + "/.index.json")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.ConfigPaths.PatternDir == "" {
|
||||||
|
c.ConfigPaths.PatternDir = filepath.Join(c.ConfigPaths.ConfigDir, "patterns/")
|
||||||
|
}
|
||||||
|
|
||||||
var configPathsCleanup = []*string{
|
var configPathsCleanup = []*string{
|
||||||
&c.ConfigPaths.HubDir,
|
&c.ConfigPaths.HubDir,
|
||||||
&c.ConfigPaths.HubIndexFile,
|
&c.ConfigPaths.HubIndexFile,
|
||||||
|
@ -41,6 +46,7 @@ func (c *Config) loadConfigurationPaths() error {
|
||||||
&c.ConfigPaths.SimulationFilePath,
|
&c.ConfigPaths.SimulationFilePath,
|
||||||
&c.ConfigPaths.PluginDir,
|
&c.ConfigPaths.PluginDir,
|
||||||
&c.ConfigPaths.NotificationDir,
|
&c.ConfigPaths.NotificationDir,
|
||||||
|
&c.ConfigPaths.PatternDir,
|
||||||
}
|
}
|
||||||
for _, k := range configPathsCleanup {
|
for _, k := range configPathsCleanup {
|
||||||
if *k == "" {
|
if *k == "" {
|
||||||
|
|
|
@ -7,10 +7,24 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/crowdsecurity/go-cs-lib/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// hubTransport wraps a Transport to set a custom User-Agent.
|
||||||
|
type hubTransport struct {
|
||||||
|
http.RoundTripper
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *hubTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
req.Header.Set("User-Agent", "crowdsec/"+version.String())
|
||||||
|
return t.RoundTripper.RoundTrip(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// hubClient is the HTTP client used to communicate with the CrowdSec Hub.
|
||||||
var hubClient = &http.Client{
|
var hubClient = &http.Client{
|
||||||
Timeout: 120 * time.Second,
|
Timeout: 120 * time.Second,
|
||||||
|
Transport: &hubTransport{http.DefaultTransport},
|
||||||
}
|
}
|
||||||
|
|
||||||
// safePath returns a joined path and ensures that it does not escape the base directory.
|
// safePath returns a joined path and ensures that it does not escape the base directory.
|
||||||
|
|
|
@ -4,9 +4,11 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
@ -65,6 +67,18 @@ func downloadFile(url string, destPath string) error {
|
||||||
// TODO: use a better way to communicate this
|
// TODO: use a better way to communicate this
|
||||||
fmt.Printf("updated %s\n", filepath.Base(destPath))
|
fmt.Printf("updated %s\n", filepath.Base(destPath))
|
||||||
|
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
// On Windows, rename will fail if the destination file already exists
|
||||||
|
// so we remove it first.
|
||||||
|
err = os.Remove(destPath)
|
||||||
|
switch {
|
||||||
|
case errors.Is(err, fs.ErrNotExist):
|
||||||
|
break
|
||||||
|
case err != nil:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err = os.Rename(tmpFileName, destPath); err != nil {
|
if err = os.Rename(tmpFileName, destPath); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,50 +1,56 @@
|
||||||
package cwhub
|
package cwhub
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/jarcoal/httpmock"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/crowdsecurity/go-cs-lib/cstest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDownloadFile(t *testing.T) {
|
func TestDownloadFile(t *testing.T) {
|
||||||
examplePath := "./example.txt"
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
defer os.Remove(examplePath)
|
switch r.URL.Path {
|
||||||
|
case "/xx":
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
_, _ = io.WriteString(w, "example content oneoneone")
|
||||||
|
default:
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
_, _ = io.WriteString(w, "not found")
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
httpmock.Activate()
|
dest := filepath.Join(t.TempDir(), "example.txt")
|
||||||
defer httpmock.DeactivateAndReset()
|
defer os.Remove(dest)
|
||||||
|
|
||||||
// OK
|
err := downloadFile(ts.URL+"/xx", dest)
|
||||||
httpmock.RegisterResponder(
|
|
||||||
"GET",
|
|
||||||
"https://example.com/xx",
|
|
||||||
httpmock.NewStringResponder(200, "example content oneoneone"),
|
|
||||||
)
|
|
||||||
|
|
||||||
httpmock.RegisterResponder(
|
|
||||||
"GET",
|
|
||||||
"https://example.com/x",
|
|
||||||
httpmock.NewStringResponder(404, "not found"),
|
|
||||||
)
|
|
||||||
|
|
||||||
err := downloadFile("https://example.com/xx", examplePath)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
content, err := os.ReadFile(examplePath)
|
content, err := os.ReadFile(dest)
|
||||||
assert.Equal(t, "example content oneoneone", string(content))
|
assert.Equal(t, "example content oneoneone", string(content))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// bad uri
|
// bad uri
|
||||||
err = downloadFile("https://zz.com", examplePath)
|
err = downloadFile("https://zz.com", dest)
|
||||||
require.Error(t, err)
|
cstest.RequireErrorContains(t, err, "lookup zz.com")
|
||||||
|
cstest.RequireErrorContains(t, err, "no such host")
|
||||||
|
|
||||||
// 404
|
// 404
|
||||||
err = downloadFile("https://example.com/x", examplePath)
|
err = downloadFile(ts.URL+"/x", dest)
|
||||||
require.Error(t, err)
|
cstest.RequireErrorContains(t, err, "bad http code 404")
|
||||||
|
|
||||||
// bad target
|
// bad target
|
||||||
err = downloadFile("https://example.com/xx", "")
|
err = downloadFile(ts.URL+"/xx", "")
|
||||||
require.Error(t, err)
|
cstest.RequireErrorContains(t, err, cstest.PathNotFoundMessage)
|
||||||
|
|
||||||
|
// destination directory does not exist
|
||||||
|
err = downloadFile(ts.URL+"/xx", filepath.Join(t.TempDir(), "missing/example.txt"))
|
||||||
|
cstest.RequireErrorContains(t, err, cstest.PathNotFoundMessage)
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,10 +26,10 @@ produces:
|
||||||
paths:
|
paths:
|
||||||
/decisions/stream:
|
/decisions/stream:
|
||||||
get:
|
get:
|
||||||
description: Returns a list of new/expired decisions. Intended for bouncers that need to "stream" decisions
|
description: Returns a list of new/expired decisions. Intended for remediation component that need to "stream" decisions
|
||||||
summary: getDecisionsStream
|
summary: getDecisionsStream
|
||||||
tags:
|
tags:
|
||||||
- bouncers
|
- Remediation component
|
||||||
operationId: getDecisionsStream
|
operationId: getDecisionsStream
|
||||||
deprecated: false
|
deprecated: false
|
||||||
produces:
|
produces:
|
||||||
|
@ -39,7 +39,7 @@ paths:
|
||||||
in: query
|
in: query
|
||||||
required: false
|
required: false
|
||||||
type: boolean
|
type: boolean
|
||||||
description: 'If true, means that the bouncers is starting and a full list must be provided'
|
description: 'If true, means that the remediation component is starting and a full list must be provided'
|
||||||
- name: scopes
|
- name: scopes
|
||||||
in: query
|
in: query
|
||||||
required: false
|
required: false
|
||||||
|
@ -73,10 +73,10 @@ paths:
|
||||||
security:
|
security:
|
||||||
- APIKeyAuthorizer: []
|
- APIKeyAuthorizer: []
|
||||||
head:
|
head:
|
||||||
description: Returns a list of new/expired decisions. Intended for bouncers that need to "stream" decisions
|
description: Returns a list of new/expired decisions. Intended for remediation component that need to "stream" decisions
|
||||||
summary: GetDecisionsStream
|
summary: GetDecisionsStream
|
||||||
tags:
|
tags:
|
||||||
- bouncers
|
- Remediation component
|
||||||
operationId: headDecisionsStream
|
operationId: headDecisionsStream
|
||||||
deprecated: false
|
deprecated: false
|
||||||
produces:
|
produces:
|
||||||
|
@ -100,7 +100,7 @@ paths:
|
||||||
description: Returns information about existing decisions
|
description: Returns information about existing decisions
|
||||||
summary: getDecisions
|
summary: getDecisions
|
||||||
tags:
|
tags:
|
||||||
- bouncers
|
- Remediation component
|
||||||
operationId: getDecisions
|
operationId: getDecisions
|
||||||
deprecated: false
|
deprecated: false
|
||||||
produces:
|
produces:
|
||||||
|
@ -164,7 +164,7 @@ paths:
|
||||||
description: Returns information about existing decisions
|
description: Returns information about existing decisions
|
||||||
summary: GetDecisions
|
summary: GetDecisions
|
||||||
tags:
|
tags:
|
||||||
- bouncers
|
- Remediation component
|
||||||
operationId: headDecisions
|
operationId: headDecisions
|
||||||
deprecated: false
|
deprecated: false
|
||||||
produces:
|
produces:
|
||||||
|
@ -1008,7 +1008,7 @@ definitions:
|
||||||
title: "error response"
|
title: "error response"
|
||||||
description: "error response return by the API"
|
description: "error response return by the API"
|
||||||
tags:
|
tags:
|
||||||
- name: bouncers
|
- name: Remediation component
|
||||||
description: 'Operations about decisions : bans, captcha, rate-limit etc.'
|
description: 'Operations about decisions : bans, captcha, rate-limit etc.'
|
||||||
- name: watchers
|
- name: watchers
|
||||||
description: 'Operations about watchers : cscli & crowdsec'
|
description: 'Operations about watchers : cscli & crowdsec'
|
||||||
|
|
|
@ -98,7 +98,7 @@ func NewParsers(hub *cwhub.Hub) *Parsers {
|
||||||
func LoadParsers(cConfig *csconfig.Config, parsers *Parsers) (*Parsers, error) {
|
func LoadParsers(cConfig *csconfig.Config, parsers *Parsers) (*Parsers, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
patternsDir := filepath.Join(cConfig.ConfigPaths.ConfigDir, "patterns/")
|
patternsDir := cConfig.ConfigPaths.PatternDir
|
||||||
log.Infof("Loading grok library %s", patternsDir)
|
log.Infof("Loading grok library %s", patternsDir)
|
||||||
/* load base regexps for two grok parsers */
|
/* load base regexps for two grok parsers */
|
||||||
parsers.Ctx, err = Init(map[string]interface{}{"patterns": patternsDir,
|
parsers.Ctx, err = Init(map[string]interface{}{"patterns": patternsDir,
|
||||||
|
|
|
@ -9,20 +9,12 @@ THIS_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||||
|
|
||||||
# pre-download everything but don't install anything
|
# pre-download everything but don't install anything
|
||||||
|
|
||||||
echo -n "Purging existing hub..."
|
echo "Pre-downloading Hub content..."
|
||||||
|
|
||||||
types=$("$CSCLI" hub types -o raw)
|
types=$("$CSCLI" hub types -o raw)
|
||||||
|
|
||||||
for itemtype in $types; do
|
for itemtype in $types; do
|
||||||
"$CSCLI" "${itemtype}" delete --all --error --purge --force
|
ALL_ITEMS=$("$CSCLI" "$itemtype" list -a -o json | itemtype="$itemtype" yq '.[env(itemtype)][] | .name')
|
||||||
done
|
|
||||||
|
|
||||||
echo " done."
|
|
||||||
|
|
||||||
echo -n "Pre-downloading Hub content..."
|
|
||||||
|
|
||||||
for itemtype in $types; do
|
|
||||||
ALL_ITEMS=$("$CSCLI" "$itemtype" list -a -o json | jq --arg itemtype "$itemtype" -r '.[$itemtype][].name')
|
|
||||||
if [[ -n "${ALL_ITEMS}" ]]; then
|
if [[ -n "${ALL_ITEMS}" ]]; then
|
||||||
#shellcheck disable=SC2086
|
#shellcheck disable=SC2086
|
||||||
"$CSCLI" "$itemtype" install \
|
"$CSCLI" "$itemtype" install \
|
||||||
|
@ -32,11 +24,4 @@ for itemtype in $types; do
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# XXX: download-only works only for collections, not for parsers, scenarios, postoverflows.
|
|
||||||
# so we have to delete the links manually, and leave the downloaded files in place
|
|
||||||
|
|
||||||
for itemtype in $types; do
|
|
||||||
"$CSCLI" "$itemtype" delete --all --error
|
|
||||||
done
|
|
||||||
|
|
||||||
echo " done."
|
echo " done."
|
||||||
|
|
Loading…
Reference in a new issue