merge from master
This commit is contained in:
commit
dc39866250
70 changed files with 2855 additions and 1107 deletions
2
.github/workflows/bats-hub.yml
vendored
2
.github/workflows/bats-hub.yml
vendored
|
@ -15,7 +15,7 @@ jobs:
|
|||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: ["1.21.3"]
|
||||
go-version: ["1.21.4"]
|
||||
|
||||
name: "Build + tests"
|
||||
runs-on: ubuntu-latest
|
||||
|
|
2
.github/workflows/bats-mysql.yml
vendored
2
.github/workflows/bats-mysql.yml
vendored
|
@ -14,7 +14,7 @@ jobs:
|
|||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: ["1.21.3"]
|
||||
go-version: ["1.21.4"]
|
||||
|
||||
name: "Build + tests"
|
||||
runs-on: ubuntu-latest
|
||||
|
|
2
.github/workflows/bats-postgres.yml
vendored
2
.github/workflows/bats-postgres.yml
vendored
|
@ -10,7 +10,7 @@ jobs:
|
|||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: ["1.21.3"]
|
||||
go-version: ["1.21.4"]
|
||||
|
||||
name: "Build + tests"
|
||||
runs-on: ubuntu-latest
|
||||
|
|
2
.github/workflows/bats-sqlite-coverage.yml
vendored
2
.github/workflows/bats-sqlite-coverage.yml
vendored
|
@ -11,7 +11,7 @@ jobs:
|
|||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: ["1.21.3"]
|
||||
go-version: ["1.21.4"]
|
||||
|
||||
name: "Build + tests"
|
||||
runs-on: ubuntu-latest
|
||||
|
|
2
.github/workflows/ci-windows-build-msi.yml
vendored
2
.github/workflows/ci-windows-build-msi.yml
vendored
|
@ -23,7 +23,7 @@ jobs:
|
|||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: ["1.21.3"]
|
||||
go-version: ["1.21.4"]
|
||||
|
||||
name: Build
|
||||
runs-on: windows-2019
|
||||
|
|
2
.github/workflows/go-tests-windows.yml
vendored
2
.github/workflows/go-tests-windows.yml
vendored
|
@ -24,7 +24,7 @@ jobs:
|
|||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: ["1.21.3"]
|
||||
go-version: ["1.21.4"]
|
||||
|
||||
name: "Build + tests"
|
||||
runs-on: windows-2022
|
||||
|
|
14
.github/workflows/go-tests.yml
vendored
14
.github/workflows/go-tests.yml
vendored
|
@ -36,7 +36,7 @@ jobs:
|
|||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: ["1.21.3"]
|
||||
go-version: ["1.21.4"]
|
||||
|
||||
name: "Build + tests"
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -110,6 +110,18 @@ jobs:
|
|||
--health-timeout 10s
|
||||
--health-retries 5
|
||||
|
||||
loki:
|
||||
image: grafana/loki:2.8.0
|
||||
ports:
|
||||
- "3100:3100"
|
||||
options: >-
|
||||
--name=loki1
|
||||
--health-cmd "wget -q -O - http://localhost:3100/ready | grep 'ready'"
|
||||
--health-interval 30s
|
||||
--health-timeout 10s
|
||||
--health-retries 5
|
||||
--health-start-period 30s
|
||||
|
||||
steps:
|
||||
|
||||
- name: Check out CrowdSec repository
|
||||
|
|
|
@ -14,7 +14,7 @@ jobs:
|
|||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: ["1.21.3"]
|
||||
go-version: ["1.21.4"]
|
||||
|
||||
name: Build and upload binary package
|
||||
runs-on: ubuntu-latest
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# vim: set ft=dockerfile:
|
||||
ARG GOVERSION=1.21.3
|
||||
ARG GOVERSION=1.21.4
|
||||
|
||||
FROM golang:${GOVERSION}-alpine AS build
|
||||
|
||||
|
@ -32,7 +32,7 @@ RUN make clean release DOCKER_BUILD=1 BUILD_STATIC=1 && \
|
|||
|
||||
FROM alpine:latest as slim
|
||||
|
||||
RUN apk add --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/community tzdata bash && \
|
||||
RUN apk add --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/community tzdata bash rsync && \
|
||||
mkdir -p /staging/etc/crowdsec && \
|
||||
mkdir -p /staging/etc/crowdsec/acquis.d && \
|
||||
mkdir -p /staging/var/lib/crowdsec && \
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# vim: set ft=dockerfile:
|
||||
ARG GOVERSION=1.21.3
|
||||
ARG GOVERSION=1.21.4
|
||||
|
||||
FROM golang:${GOVERSION}-bookworm AS build
|
||||
|
||||
|
@ -47,7 +47,8 @@ RUN apt-get update && \
|
|||
iproute2 \
|
||||
ca-certificates \
|
||||
bash \
|
||||
tzdata && \
|
||||
tzdata \
|
||||
rsync && \
|
||||
mkdir -p /staging/etc/crowdsec && \
|
||||
mkdir -p /staging/etc/crowdsec/acquis.d && \
|
||||
mkdir -p /staging/var/lib/crowdsec && \
|
||||
|
|
|
@ -27,7 +27,7 @@ stages:
|
|||
- task: GoTool@0
|
||||
displayName: "Install Go 1.20"
|
||||
inputs:
|
||||
version: '1.21.3'
|
||||
version: '1.21.4'
|
||||
|
||||
- pwsh: |
|
||||
choco install -y make
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -13,12 +12,12 @@ import (
|
|||
"github.com/fatih/color"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"slices"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
|
||||
middlewares "github.com/crowdsecurity/crowdsec/pkg/apiserver/middlewares/v1"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/database"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
|
||||
)
|
||||
|
||||
func getBouncers(out io.Writer, dbClient *database.Client) error {
|
||||
|
@ -26,16 +25,18 @@ func getBouncers(out io.Writer, dbClient *database.Client) error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("unable to list bouncers: %s", err)
|
||||
}
|
||||
if csConfig.Cscli.Output == "human" {
|
||||
|
||||
switch csConfig.Cscli.Output {
|
||||
case "human":
|
||||
getBouncersTable(out, bouncers)
|
||||
} else if csConfig.Cscli.Output == "json" {
|
||||
case "json":
|
||||
enc := json.NewEncoder(out)
|
||||
enc.SetIndent("", " ")
|
||||
if err := enc.Encode(bouncers); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal: %w", err)
|
||||
}
|
||||
return nil
|
||||
} else if csConfig.Cscli.Output == "raw" {
|
||||
case "raw":
|
||||
csvwriter := csv.NewWriter(out)
|
||||
err := csvwriter.Write([]string{"name", "ip", "revoked", "last_pull", "type", "version", "auth_type"})
|
||||
if err != nil {
|
||||
|
@ -55,6 +56,7 @@ func getBouncers(out io.Writer, dbClient *database.Client) error {
|
|||
}
|
||||
csvwriter.Flush()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -78,12 +80,9 @@ func NewBouncersListCmd() *cobra.Command {
|
|||
}
|
||||
|
||||
func runBouncersAdd(cmd *cobra.Command, args []string) error {
|
||||
flags := cmd.Flags()
|
||||
keyLength := 32
|
||||
|
||||
keyLength, err := flags.GetInt("length")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
|
||||
key, err := flags.GetString("key")
|
||||
if err != nil {
|
||||
|
@ -108,13 +107,14 @@ func runBouncersAdd(cmd *cobra.Command, args []string) error {
|
|||
return fmt.Errorf("unable to create bouncer: %s", err)
|
||||
}
|
||||
|
||||
if csConfig.Cscli.Output == "human" {
|
||||
switch csConfig.Cscli.Output {
|
||||
case "human":
|
||||
fmt.Printf("API key for '%s':\n\n", keyName)
|
||||
fmt.Printf(" %s\n\n", apiKey)
|
||||
fmt.Print("Please keep this key since you will not be able to retrieve it!\n")
|
||||
} else if csConfig.Cscli.Output == "raw" {
|
||||
case "raw":
|
||||
fmt.Printf("%s", apiKey)
|
||||
} else if csConfig.Cscli.Output == "json" {
|
||||
case "json":
|
||||
j, err := json.Marshal(apiKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to marshal api key")
|
||||
|
@ -127,19 +127,18 @@ func runBouncersAdd(cmd *cobra.Command, args []string) error {
|
|||
|
||||
func NewBouncersAddCmd() *cobra.Command {
|
||||
cmdBouncersAdd := &cobra.Command{
|
||||
Use: "add MyBouncerName [--length 16]",
|
||||
Use: "add MyBouncerName",
|
||||
Short: "add a single bouncer to the database",
|
||||
Example: `cscli bouncers add MyBouncerName
|
||||
cscli bouncers add MyBouncerName -l 24
|
||||
cscli bouncers add MyBouncerName -k <random-key>`,
|
||||
cscli bouncers add MyBouncerName --key <random-key>`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: runBouncersAdd,
|
||||
}
|
||||
|
||||
flags := cmdBouncersAdd.Flags()
|
||||
|
||||
flags.IntP("length", "l", 16, "length of the api key")
|
||||
flags.StringP("length", "l", "", "length of the api key")
|
||||
flags.MarkDeprecated("length", "use --key instead")
|
||||
flags.StringP("key", "k", "", "api key for the bouncer")
|
||||
|
||||
return cmdBouncersAdd
|
||||
|
|
|
@ -292,7 +292,6 @@ func itemsInstallRunner(it hubItemType) func(cmd *cobra.Command, args []string)
|
|||
}
|
||||
}
|
||||
|
||||
// XXX: only reload if we installed something
|
||||
log.Infof(ReloadMessage())
|
||||
return nil
|
||||
}
|
||||
|
@ -328,7 +327,7 @@ func NewItemsInstallCmd(typeName string) *cobra.Command {
|
|||
func istalledParentNames(item *cwhub.Item) []string {
|
||||
ret := make([]string, 0)
|
||||
|
||||
for _, parent := range item.AncestorCollections() {
|
||||
for _, parent := range item.Ancestors() {
|
||||
if parent.State.Installed {
|
||||
ret = append(ret, parent.Name)
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ func listItems(out io.Writer, itemTypes []string, items map[string][]*cwhub.Item
|
|||
hubStatus[itemType] = make([]itemHubStatus, len(items[itemType]))
|
||||
|
||||
for i, item := range items[itemType] {
|
||||
status, emo := item.Status()
|
||||
status, emo := item.InstallStatus()
|
||||
hubStatus[itemType][i] = itemHubStatus{
|
||||
Name: item.Name,
|
||||
LocalVersion: item.State.LocalVersion,
|
||||
|
@ -107,7 +107,7 @@ func listItems(out io.Writer, itemTypes []string, items map[string][]*cwhub.Item
|
|||
|
||||
for _, itemType := range itemTypes {
|
||||
for _, item := range items[itemType] {
|
||||
status, _ := item.Status()
|
||||
status, _ := item.InstallStatus()
|
||||
row := []string{
|
||||
item.Name,
|
||||
status,
|
||||
|
|
|
@ -125,7 +125,7 @@ var (
|
|||
|
||||
func main() {
|
||||
// set the formatter asap and worry about level later
|
||||
logFormatter := &log.TextFormatter{TimestampFormat: "02-01-2006 15:04:05", FullTimestamp: true}
|
||||
logFormatter := &log.TextFormatter{TimestampFormat: "2006-01-02 15:04:05", FullTimestamp: true}
|
||||
log.SetFormatter(logFormatter)
|
||||
|
||||
if err := fflag.RegisterAllFeatures(); err != nil {
|
||||
|
|
|
@ -18,7 +18,7 @@ func listHubItemTable(out io.Writer, title string, items []*cwhub.Item) {
|
|||
t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft)
|
||||
|
||||
for _, item := range items {
|
||||
status, emo := item.Status()
|
||||
status, emo := item.InstallStatus()
|
||||
t.AddRow(item.Name, fmt.Sprintf("%v %s", emo, status), item.State.LocalVersion, item.State.LocalPath)
|
||||
}
|
||||
renderTableTitle(out, title)
|
||||
|
|
|
@ -178,7 +178,7 @@ if [ ! -e "/etc/crowdsec/local_api_credentials.yaml" ] && [ ! -e "/etc/crowdsec/
|
|||
mkdir -p /etc/crowdsec/
|
||||
# if you change this, check that it still works
|
||||
# under alpine and k8s, with and without tls
|
||||
cp -an /staging/etc/crowdsec/* /etc/crowdsec/
|
||||
rsync -av --ignore-existing /staging/etc/crowdsec/* /etc/crowdsec
|
||||
fi
|
||||
fi
|
||||
|
||||
|
|
47
docker/test/tests/test_local_item.py
Normal file
47
docker/test/tests/test_local_item.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Test bind-mounting local items
|
||||
"""
|
||||
|
||||
from http import HTTPStatus
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
pytestmark = pytest.mark.docker
|
||||
|
||||
|
||||
def test_inject_local_item(crowdsec, tmp_path_factory, flavor):
|
||||
"""Test mounting a custom whitelist at startup"""
|
||||
|
||||
localitems = tmp_path_factory.mktemp('localitems')
|
||||
custom_whitelists = localitems / 'custom_whitelists.yaml'
|
||||
|
||||
with open(custom_whitelists, 'w') as f:
|
||||
f.write('{"whitelist":{"reason":"Good IPs","ip":["1.2.3.4"]}}')
|
||||
|
||||
volumes = {
|
||||
custom_whitelists: {'bind': '/etc/crowdsec/parsers/s02-enrich/custom_whitelists.yaml'}
|
||||
}
|
||||
|
||||
with crowdsec(flavor=flavor, volumes=volumes) as cs:
|
||||
cs.wait_for_log([
|
||||
"*Starting processing data*"
|
||||
])
|
||||
cs.wait_for_http(8080, '/health', want_status=HTTPStatus.OK)
|
||||
|
||||
# the parser should be enabled
|
||||
res = cs.cont.exec_run('cscli parsers list -o json')
|
||||
assert res.exit_code == 0
|
||||
j = json.loads(res.output)
|
||||
items = {c['name']: c for c in j['parsers']}
|
||||
assert items['custom_whitelists.yaml']['status'] == 'enabled,local'
|
||||
|
||||
# regression test: the linux collection should not be tainted
|
||||
# (the parsers were not copied from /staging when using "cp -an" with local parsers)
|
||||
res = cs.cont.exec_run('cscli collections inspect crowdsecurity/linux -o json')
|
||||
assert res.exit_code == 0
|
||||
j = json.loads(res.output)
|
||||
# crowdsec <= 1.5.5 omits a "tainted" when it's false
|
||||
assert j.get('tainted', False) is False
|
58
go.mod
58
go.mod
|
@ -8,12 +8,12 @@ go 1.21
|
|||
|
||||
require (
|
||||
entgo.io/ent v0.12.4
|
||||
github.com/AlecAivazis/survey/v2 v2.2.7
|
||||
github.com/Masterminds/semver/v3 v3.1.1
|
||||
github.com/Masterminds/sprig/v3 v3.2.2
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7
|
||||
github.com/Masterminds/semver/v3 v3.2.1
|
||||
github.com/Masterminds/sprig/v3 v3.2.3
|
||||
github.com/agext/levenshtein v1.2.1
|
||||
github.com/alexliesenfeld/health v0.5.1
|
||||
github.com/antonmedv/expr v1.12.5
|
||||
github.com/alexliesenfeld/health v0.8.0
|
||||
github.com/antonmedv/expr v1.15.3
|
||||
github.com/appleboy/gin-jwt/v2 v2.8.0
|
||||
github.com/aquasecurity/table v1.8.0
|
||||
github.com/aws/aws-lambda-go v1.38.0
|
||||
|
@ -30,7 +30,7 @@ require (
|
|||
github.com/crowdsecurity/machineid v1.0.2
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/dghubble/sling v1.3.0
|
||||
github.com/docker/docker v24.0.4+incompatible
|
||||
github.com/docker/docker v24.0.7+incompatible
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/enescakir/emoji v1.0.0
|
||||
github.com/fatih/color v1.15.0
|
||||
|
@ -44,11 +44,12 @@ require (
|
|||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/goccy/go-yaml v1.11.0
|
||||
github.com/gofrs/uuid v4.0.0+incompatible
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
github.com/google/go-querystring v1.0.0
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/google/winops v0.0.0-20230712152054-af9b550d0601
|
||||
github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/hashicorp/go-hclog v1.5.0
|
||||
github.com/hashicorp/go-plugin v1.4.10
|
||||
github.com/hashicorp/go-version v1.2.1
|
||||
|
@ -65,11 +66,11 @@ require (
|
|||
github.com/oschwald/maxminddb-golang v1.8.0
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.14.0
|
||||
github.com/prometheus/client_model v0.3.0
|
||||
github.com/prometheus/client_golang v1.16.0
|
||||
github.com/prometheus/client_model v0.4.0
|
||||
github.com/prometheus/prom2json v1.3.0
|
||||
github.com/r3labs/diff/v2 v2.14.1
|
||||
github.com/segmentio/kafka-go v0.4.34
|
||||
github.com/segmentio/kafka-go v0.4.45
|
||||
github.com/shirou/gopsutil/v3 v3.23.5
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/slack-go/slack v0.12.2
|
||||
|
@ -81,8 +82,8 @@ require (
|
|||
golang.org/x/crypto v0.15.0
|
||||
golang.org/x/mod v0.11.0
|
||||
golang.org/x/sys v0.14.0
|
||||
google.golang.org/grpc v1.56.1
|
||||
google.golang.org/protobuf v1.30.0
|
||||
google.golang.org/grpc v1.56.3
|
||||
google.golang.org/protobuf v1.31.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
|
@ -90,8 +91,10 @@ require (
|
|||
|
||||
require (
|
||||
github.com/crowdsecurity/coraza/v3 v3.0.0-20231114091225-b0f8bc435a75
|
||||
golang.org/x/text v0.14.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/apiserver v0.27.3
|
||||
gotest.tools/v3 v3.5.0
|
||||
k8s.io/apiserver v0.28.4
|
||||
)
|
||||
|
||||
require (
|
||||
|
@ -112,12 +115,12 @@ require (
|
|||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/go-logr/logr v1.2.4 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-openapi/analysis v0.19.16 // indirect
|
||||
github.com/go-openapi/inflect v0.19.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.1 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/loads v0.20.0 // indirect
|
||||
github.com/go-openapi/runtime v0.19.24 // indirect
|
||||
github.com/go-openapi/spec v0.20.0 // indirect
|
||||
|
@ -131,10 +134,9 @@ require (
|
|||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.13.0 // indirect
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect
|
||||
github.com/huandu/xstrings v1.3.2 // indirect
|
||||
github.com/huandu/xstrings v1.3.3 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||
|
@ -148,7 +150,7 @@ require (
|
|||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/klauspost/compress v1.15.7 // indirect
|
||||
github.com/klauspost/compress v1.17.3 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
|
@ -172,11 +174,11 @@ require (
|
|||
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||
github.com/petar-dambovaliev/aho-corasick v0.0.0-20230725210150-fb29fc3c913e // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.15 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.18 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/prometheus/common v0.44.0 // indirect
|
||||
github.com/prometheus/procfs v0.10.1 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
|
@ -201,19 +203,17 @@ require (
|
|||
golang.org/x/net v0.18.0 // indirect
|
||||
golang.org/x/sync v0.5.0 // indirect
|
||||
golang.org/x/term v0.14.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.2.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.8.1-0.20230428195545-5283a0178901 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gotest.tools/v3 v3.5.0 // indirect
|
||||
k8s.io/api v0.27.3 // indirect
|
||||
k8s.io/apimachinery v0.27.3 // indirect
|
||||
k8s.io/klog/v2 v2.90.1 // indirect
|
||||
k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect
|
||||
k8s.io/api v0.28.4 // indirect
|
||||
k8s.io/apimachinery v0.28.4 // indirect
|
||||
k8s.io/klog/v2 v2.100.1 // indirect
|
||||
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect
|
||||
rsc.io/binaryregexp v0.2.0 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
||||
|
|
493
go.sum
493
go.sum
|
@ -2,59 +2,27 @@ ariga.io/atlas v0.14.1-0.20230918065911-83ad451a4935 h1:JnYs/y8RJ3+MiIUp+3RgyyeO
|
|||
ariga.io/atlas v0.14.1-0.20230918065911-83ad451a4935/go.mod h1:isZrlzJ5cpoCoKFoY9knZug7Lq4pP1cm8g3XciLZ0Pw=
|
||||
bitbucket.org/creachadair/stringset v0.0.9 h1:L4vld9nzPt90UZNrXjNelTshD74ps4P5NGs3Iq6yN3o=
|
||||
bitbucket.org/creachadair/stringset v0.0.9/go.mod h1:t+4WcQ4+PXTa8aQdNKe40ZP6iwesoMFWAxPGd3UGjyY=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
entgo.io/ent v0.12.4 h1:LddPnAyxls/O7DTXZvUGDj0NZIdGSu317+aoNLJWbD8=
|
||||
entgo.io/ent v0.12.4/go.mod h1:Y3JVAjtlIk8xVZYSn3t3mf8xlZIn5SAOXZQxD6kKI+Q=
|
||||
github.com/AlecAivazis/survey/v2 v2.2.7 h1:5NbxkF4RSKmpywYdcRgUmos1o+roJY8duCLZXbVjoig=
|
||||
github.com/AlecAivazis/survey/v2 v2.2.7/go.mod h1:9DYvHgXtiXm6nCn+jXnOXLKbH+Yo9u8fAS/SduGdoPk=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
|
||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8=
|
||||
github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
|
||||
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
|
||||
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
|
||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
|
||||
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
|
||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
|
||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
|
||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
|
@ -68,11 +36,11 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
|
|||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/alexliesenfeld/health v0.5.1 h1:cohQdtQbJdA6bj0aMD4gdXA9xQyvh9NxWO9XLGYTYcY=
|
||||
github.com/alexliesenfeld/health v0.5.1/go.mod h1:N4NDIeQtlWumG+6z1ne1v62eQxktz5ylEgGgH9emdMw=
|
||||
github.com/alexliesenfeld/health v0.8.0 h1:lCV0i+ZJPTbqP7LfKG7p3qZBl5VhelwUFCIVWl77fgk=
|
||||
github.com/alexliesenfeld/health v0.8.0/go.mod h1:TfNP0f+9WQVWMQRzvMUjlws4ceXKEL3WR+6Hp95HUFc=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/antonmedv/expr v1.12.5 h1:Fq4okale9swwL3OeLLs9WD9H6GbgBLJyN/NUHRv+n0E=
|
||||
github.com/antonmedv/expr v1.12.5/go.mod h1:FPC8iWArxls7axbVLsW+kpg1mz29A1b2M6jt+hZfDkU=
|
||||
github.com/antonmedv/expr v1.15.3 h1:q3hOJZNvLvhqE8OHBs1cFRdbXFNKuA+bHmRaI+AmRmI=
|
||||
github.com/antonmedv/expr v1.15.3/go.mod h1:0E/6TxnOlRNp81GMzX9QfDPAmHo2Phg00y4JUv1ihsE=
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
|
||||
github.com/appleboy/gin-jwt/v2 v2.8.0 h1:Glo7cb9eBR+hj8Y7WzgfkOlqCaNLjP+RV4dNO3fpdps=
|
||||
|
@ -109,19 +77,11 @@ github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s
|
|||
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
github.com/c-robinson/iplib v1.0.3 h1:NG0UF0GoEsrC1/vyfX1Lx2Ss7CySWl3KqqXh3q4DdPU=
|
||||
github.com/c-robinson/iplib v1.0.3/go.mod h1:i3LuuFL1hRT5gFpBRnEydzw8R6yhGkF4szNDIbF8pgo=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/corazawaf/libinjection-go v0.1.2 h1:oeiV9pc5rvJ+2oqOqXEAMJousPpGiup6f7Y3nZj5GoM=
|
||||
|
@ -135,10 +95,9 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHH
|
|||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/crowdsecurity/coraza/v3 v3.0.0-20231113100456-9fb1947fe2bf h1:NzG+bC9a1dL9RagY7Rp84CLU4ARLg4EvC7DIP6/czcA=
|
||||
github.com/crowdsecurity/coraza/v3 v3.0.0-20231113100456-9fb1947fe2bf/go.mod h1:jNww1Y9SujXQc89zDR+XOb70bkC7mZ6ep7iKhUBBsiI=
|
||||
github.com/crowdsecurity/coraza/v3 v3.0.0-20231114091225-b0f8bc435a75 h1:Kp1sY2PE1H5nbr7xgAQeEWDqDW/o3HNL1rHvcVqzWT4=
|
||||
github.com/crowdsecurity/coraza/v3 v3.0.0-20231114091225-b0f8bc435a75/go.mod h1:jNww1Y9SujXQc89zDR+XOb70bkC7mZ6ep7iKhUBBsiI=
|
||||
github.com/crowdsecurity/dlog v0.0.0-20170105205344-4fb5f8204f26 h1:r97WNVC30Uen+7WnLs4xDScS/Ex988+id2k6mDf8psU=
|
||||
|
@ -156,8 +115,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/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
|
||||
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v24.0.4+incompatible h1:s/LVDftw9hjblvqIeTiGYXBCD95nOEEl7qRsRrIOuQI=
|
||||
github.com/docker/docker v24.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM=
|
||||
github.com/docker/docker v24.0.7+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/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
|
@ -166,10 +125,6 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
|
|||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/enescakir/emoji v1.0.0 h1:W+HsNql8swfCQFtioDGDHCHri8nudlK1n5p2rHCJoog=
|
||||
github.com/enescakir/emoji v1.0.0/go.mod h1:Bt1EKuLnKDTYpLALApstIkAjdDrS/8IAgTkKp+WKFD0=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
|
@ -189,20 +144,15 @@ github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0
|
|||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/go-co-op/gocron v1.17.0 h1:IixLXsti+Qo0wMvmn6Kmjp2csk2ykpkcL+EmHmST18w=
|
||||
github.com/go-co-op/gocron v1.17.0/go.mod h1:IpDBSaJOVfFw7hXZuTag3SCSkqazXBBUkbQ1m1aesBs=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
|
||||
|
@ -238,8 +188,8 @@ github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3Hfo
|
|||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
||||
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
|
||||
github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8=
|
||||
github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
|
@ -346,51 +296,23 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
|
|||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
|
||||
github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
|
@ -401,15 +323,6 @@ github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO
|
|||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
|
@ -417,8 +330,6 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
|||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/winops v0.0.0-20230712152054-af9b550d0601 h1:XvlrmqZIuwxuRE88S9mkxX+FkV+YakqbiAC5Z4OzDnM=
|
||||
github.com/google/winops v0.0.0-20230712152054-af9b550d0601/go.mod h1:rT1mcjzuvcDDbRmUTsoH6kV0DG91AkFe9UCjASraK5I=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e h1:XmA6L9IPRdUr28a+SK/oMchGgQy159wvzXA5tJ7l+40=
|
||||
github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e/go.mod h1:AFIo+02s+12CEg8Gzz9kzhCbmbq6JcKNrhHffCGA9z4=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
|
@ -430,18 +341,14 @@ github.com/hashicorp/go-plugin v1.4.10 h1:xUbmA4jC6Dq163/fWcp8P3JuHilrHHMLNRxzGQ
|
|||
github.com/hashicorp/go-plugin v1.4.10/go.mod h1:6/1TEzT0eQznvI/gV2CM29DLSkAK/e58mUWKVsPaph0=
|
||||
github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI=
|
||||
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl/v2 v2.13.0 h1:0Apadu1w6M11dyGFxWnmhhcMjkbAiKCv7G1r/2QgCNc=
|
||||
github.com/hashicorp/hcl/v2 v2.13.0/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0zIUqPc2jr0=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ=
|
||||
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
|
||||
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
|
||||
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
||||
github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
|
||||
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
|
@ -508,19 +415,13 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfC
|
|||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/jszwec/csvutil v1.5.1 h1:c3GFBhj6DFMUl4dMK3+B6rz2+LWWS/e9VJiVJ9t9kfQ=
|
||||
github.com/jszwec/csvutil v1.5.1/go.mod h1:Rpu7Uu9giO9subDyMCIQfHVDuLrcaC36UA4YcJjGBkg=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
|
||||
github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
|
@ -530,23 +431,21 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
|
|||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.15.7 h1:7cgTQxJCU/vy+oP/E3B9RGbQTgbiVzIJWIKOLoAsPok=
|
||||
github.com/klauspost/compress v1.15.7/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||
github.com/klauspost/compress v1.17.3 h1:qkRjuerhUU1EmXLYGkSH6EZL+vPSxIrYjLNAK4slzwA=
|
||||
github.com/klauspost/compress v1.17.3/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||
github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
|
@ -635,7 +534,6 @@ github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJ
|
|||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
|
@ -659,8 +557,9 @@ github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZ
|
|||
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
||||
github.com/petar-dambovaliev/aho-corasick v0.0.0-20230725210150-fb29fc3c913e h1:POJco99aNgosh92lGqmx7L1ei+kCymivB/419SD15PQ=
|
||||
github.com/petar-dambovaliev/aho-corasick v0.0.0-20230725210150-fb29fc3c913e/go.mod h1:EHPiTAKtiFmrMldLUNswFwfZ2eJIYBHktdaUTZxYWRw=
|
||||
github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
|
||||
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ=
|
||||
github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
|
@ -671,32 +570,21 @@ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF
|
|||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
|
||||
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
|
||||
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
|
||||
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
|
||||
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
|
||||
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
|
||||
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
|
||||
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
|
||||
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
|
||||
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
|
||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
|
||||
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
|
||||
github.com/prometheus/prom2json v1.3.0 h1:BlqrtbT9lLH3ZsOVhXPsHzFrApCTKRifB7gjJuypu6Y=
|
||||
github.com/prometheus/prom2json v1.3.0/go.mod h1:rMN7m0ApCowcoDlypBHlkNbp5eJQf/+1isKykIP5ZnM=
|
||||
github.com/r3labs/diff/v2 v2.14.1 h1:wRZ3jB44Ny50DSXsoIcFQ27l2x+n5P31K/Pk+b9B0Ic=
|
||||
|
@ -716,8 +604,8 @@ github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThC
|
|||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/segmentio/kafka-go v0.4.34 h1:Dm6YlLMiVSiwwav20KY0AoY63s661FXevwJ3CVHUERo=
|
||||
github.com/segmentio/kafka-go v0.4.34/go.mod h1:GAjxBQJdQMB5zfNA21AhpaqOB2Mu+w3De4ni3Gbm8y0=
|
||||
github.com/segmentio/kafka-go v0.4.45 h1:prqrZp1mMId4kI6pyPolkLsH6sWOUmDxmmucbL4WS6E=
|
||||
github.com/segmentio/kafka-go v0.4.45/go.mod h1:HjF6XbOKh0Pjlkr5GVZxt6CsjjwnmhVOfURM5KMd8qg=
|
||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shirou/gopsutil/v3 v3.23.5 h1:5SgDCeQ0KW0S4N0znjeM/eFHXXOKyv2dVNgRq/c9P6Y=
|
||||
|
@ -733,7 +621,6 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
|
|||
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/slack-go/slack v0.12.2 h1:x3OppyMyGIbbiyFhsBmpf9pwkUzMhthJMRNmNlA4LaQ=
|
||||
|
@ -753,7 +640,6 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH
|
|||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
|
@ -804,22 +690,22 @@ github.com/wasilibs/go-re2 v1.3.0 h1:LFhBNzoStM3wMie6rN2slD1cuYH2CGiHpvNL3UtcsMw
|
|||
github.com/wasilibs/go-re2 v1.3.0/go.mod h1:AafrCXVvGRJJOImMajgJ2M7rVmWyisVK7sFshbxnVrg=
|
||||
github.com/wasilibs/nottinygc v0.4.0 h1:h1TJMihMC4neN6Zq+WKpLxgd9xCFMw7O9ETLwY2exJQ=
|
||||
github.com/wasilibs/nottinygc v0.4.0/go.mod h1:oDcIotskuYNMpqMF23l7Z8uzD4TC0WXHK8jetlB3HIo=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
|
||||
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
||||
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
||||
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
|
||||
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
|
||||
github.com/xdg/scram v1.0.5 h1:TuS0RFmt5Is5qm9Tm2SoD89OPqe4IRiFtyFY4iwWXsw=
|
||||
github.com/xdg/scram v1.0.5/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
|
||||
github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
|
||||
github.com/xdg/stringprep v1.0.3 h1:cmL5Enob4W83ti/ZHuZLuKD/xqJfus4fVPwE+/BDm+4=
|
||||
github.com/xdg/stringprep v1.0.3/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
|
||||
github.com/xhit/go-simple-mail/v2 v2.16.0 h1:ouGy/Ww4kuaqu2E2UrDw7SvLaziWTB60ICLkIkNVccA=
|
||||
github.com/xhit/go-simple-mail/v2 v2.16.0/go.mod h1:b7P5ygho6SYE+VIqpxA6QkYfv4teeyG4MKqB3utRu98=
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
|
||||
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
github.com/zclconf/go-cty v1.8.0 h1:s4AvqaeQzJIu3ndv4gVIhplVD0krU+bgrcLSVUnaWuA=
|
||||
|
@ -833,11 +719,6 @@ go.mongodb.org/mongo-driver v1.4.3/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4S
|
|||
go.mongodb.org/mongo-driver v1.4.4/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
|
||||
go.mongodb.org/mongo-driver v1.9.4 h1:qXWlnK2WCOWSxJ/Hm3XyYOGKv3ujA2btBsCyuIFvQjc=
|
||||
go.mongodb.org/mongo-driver v1.9.4/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
|
@ -859,260 +740,153 @@ golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaE
|
|||
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
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.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
|
||||
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
|
||||
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220706163947-c90051bbdb60/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
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.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
|
||||
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/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-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
||||
golang.org/x/sys v0.14.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-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.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
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.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
|
||||
golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/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.1-0.20180807135948-17ff2d5776d2/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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.2.0 h1:52I/1L54xyEQAYdtcSuxtiT84KGYTBGXwayxmIpNJhE=
|
||||
golang.org/x/time v0.2.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.8.1-0.20230428195545-5283a0178901 h1:0wxTF6pSjIIhNt7mo9GvjDfzyCOiWhmICgtO/Ah948s=
|
||||
golang.org/x/tools v0.8.1-0.20230428195545-5283a0178901/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -1122,93 +896,21 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
|
|||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.56.1 h1:z0dNfjIl0VpaZ9iSVjA6daGatAYwPGstTjt5vkRMFkQ=
|
||||
google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
|
||||
google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc=
|
||||
google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
@ -1225,7 +927,6 @@ gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637/go.mod h1:BHsqpu/nsuzkT5BpiH
|
|||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
|
@ -1237,28 +938,20 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY=
|
||||
gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
k8s.io/api v0.27.3 h1:yR6oQXXnUEBWEWcvPWS0jQL575KoAboQPfJAuKNrw5Y=
|
||||
k8s.io/api v0.27.3/go.mod h1:C4BNvZnQOF7JA/0Xed2S+aUyJSfTGkGFxLXz9MnpIpg=
|
||||
k8s.io/apimachinery v0.27.3 h1:Ubye8oBufD04l9QnNtW05idcOe9Z3GQN8+7PqmuVcUM=
|
||||
k8s.io/apimachinery v0.27.3/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E=
|
||||
k8s.io/apiserver v0.27.3 h1:AxLvq9JYtveYWK+D/Dz/uoPCfz8JC9asR5z7+I/bbQ4=
|
||||
k8s.io/apiserver v0.27.3/go.mod h1:Y61+EaBMVWUBJtxD5//cZ48cHZbQD+yIyV/4iEBhhNA=
|
||||
k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw=
|
||||
k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||
k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY=
|
||||
k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
k8s.io/api v0.28.4 h1:8ZBrLjwosLl/NYgv1P7EQLqoO8MGQApnbgH8tu3BMzY=
|
||||
k8s.io/api v0.28.4/go.mod h1:axWTGrY88s/5YE+JSt4uUi6NMM+gur1en2REMR7IRj0=
|
||||
k8s.io/apimachinery v0.28.4 h1:zOSJe1mc+GxuMnFzD4Z/U1wst50X28ZNsn5bhgIIao8=
|
||||
k8s.io/apimachinery v0.28.4/go.mod h1:wI37ncBvfAoswfq626yPTe6Bz1c22L7uaJ8dho83mgg=
|
||||
k8s.io/apiserver v0.28.4 h1:BJXlaQbAU/RXYX2lRz+E1oPe3G3TKlozMMCZWu5GMgg=
|
||||
k8s.io/apiserver v0.28.4/go.mod h1:Idq71oXugKZoVGUUL2wgBCTHbUR+FYTWa4rq9j4n23w=
|
||||
k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
|
||||
k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk=
|
||||
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
kafkaacquisition "github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/kafka"
|
||||
kinesisacquisition "github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/kinesis"
|
||||
k8sauditacquisition "github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/kubernetesaudit"
|
||||
lokiacquisition "github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/loki"
|
||||
s3acquisition "github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/s3"
|
||||
syslogacquisition "github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/syslog"
|
||||
wafacquisition "github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/waap"
|
||||
|
@ -74,6 +75,7 @@ var AcquisitionSources = map[string]func() DataSource{
|
|||
"wineventlog": func() DataSource { return &wineventlogacquisition.WinEventLogSource{} },
|
||||
"kafka": func() DataSource { return &kafkaacquisition.KafkaSource{} },
|
||||
"k8s-audit": func() DataSource { return &k8sauditacquisition.KubernetesAuditSource{} },
|
||||
"loki": func() DataSource { return &lokiacquisition.LokiSource{} },
|
||||
"s3": func() DataSource { return &s3acquisition.S3Source{} },
|
||||
"waf": func() DataSource { return &wafacquisition.WaapSource{} },
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
@ -37,6 +38,7 @@ type KafkaConfiguration struct {
|
|||
Brokers []string `yaml:"brokers"`
|
||||
Topic string `yaml:"topic"`
|
||||
GroupID string `yaml:"group_id"`
|
||||
Partition int `yaml:"partition"`
|
||||
Timeout string `yaml:"timeout"`
|
||||
TLS *TLSConfig `yaml:"tls"`
|
||||
configuration.DataSourceCommonCfg `yaml:",inline"`
|
||||
|
@ -79,12 +81,16 @@ func (k *KafkaSource) UnmarshalConfig(yamlConfig []byte) error {
|
|||
k.Config.Mode = configuration.TAIL_MODE
|
||||
}
|
||||
|
||||
k.logger.Debugf("successfully unmarshaled kafka configuration : %+v", k.Config)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (k *KafkaSource) Configure(yamlConfig []byte, logger *log.Entry) error {
|
||||
k.logger = logger
|
||||
|
||||
k.logger.Debugf("start configuring %s source", dataSourceName)
|
||||
|
||||
err := k.UnmarshalConfig(yamlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -95,7 +101,7 @@ func (k *KafkaSource) Configure(yamlConfig []byte, logger *log.Entry) error {
|
|||
return fmt.Errorf("cannot create %s dialer: %w", dataSourceName, err)
|
||||
}
|
||||
|
||||
k.Reader, err = k.Config.NewReader(dialer)
|
||||
k.Reader, err = k.Config.NewReader(dialer, k.logger)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannote create %s reader: %w", dataSourceName, err)
|
||||
}
|
||||
|
@ -104,6 +110,8 @@ func (k *KafkaSource) Configure(yamlConfig []byte, logger *log.Entry) error {
|
|||
return fmt.Errorf("cannot create %s reader", dataSourceName)
|
||||
}
|
||||
|
||||
k.logger.Debugf("successfully configured %s source", dataSourceName)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -143,9 +151,10 @@ func (k *KafkaSource) ReadMessage(out chan types.Event) error {
|
|||
// Start processing from latest Offset
|
||||
k.Reader.SetOffsetAt(context.Background(), time.Now())
|
||||
for {
|
||||
k.logger.Tracef("reading message from topic '%s'", k.Config.Topic)
|
||||
m, err := k.Reader.ReadMessage(context.Background())
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
if errors.Is(err, io.EOF) {
|
||||
return nil
|
||||
}
|
||||
k.logger.Errorln(fmt.Errorf("while reading %s message: %w", dataSourceName, err))
|
||||
|
@ -160,6 +169,7 @@ func (k *KafkaSource) ReadMessage(out chan types.Event) error {
|
|||
Process: true,
|
||||
Module: k.GetName(),
|
||||
}
|
||||
k.logger.Tracef("line with message read from topic '%s': %+v", k.Config.Topic, l)
|
||||
linesRead.With(prometheus.Labels{"topic": k.Config.Topic}).Inc()
|
||||
var evt types.Event
|
||||
|
||||
|
@ -173,6 +183,7 @@ func (k *KafkaSource) ReadMessage(out chan types.Event) error {
|
|||
}
|
||||
|
||||
func (k *KafkaSource) RunReader(out chan types.Event, t *tomb.Tomb) error {
|
||||
k.logger.Debugf("starting %s datasource reader goroutine with configuration %+v", dataSourceName, k.Config)
|
||||
t.Go(func() error {
|
||||
return k.ReadMessage(out)
|
||||
})
|
||||
|
@ -190,7 +201,7 @@ func (k *KafkaSource) RunReader(out chan types.Event, t *tomb.Tomb) error {
|
|||
}
|
||||
|
||||
func (k *KafkaSource) StreamingAcquisition(out chan types.Event, t *tomb.Tomb) error {
|
||||
k.logger.Infof("start reader on topic '%s'", k.Config.Topic)
|
||||
k.logger.Infof("start reader on brokers '%+v' with topic '%s'", k.Config.Brokers, k.Config.Topic)
|
||||
|
||||
t.Go(func() error {
|
||||
defer trace.CatchPanic("crowdsec/acquis/kafka/live")
|
||||
|
@ -254,14 +265,23 @@ func (kc *KafkaConfiguration) NewDialer() (*kafka.Dialer, error) {
|
|||
return dialer, nil
|
||||
}
|
||||
|
||||
func (kc *KafkaConfiguration) NewReader(dialer *kafka.Dialer) (*kafka.Reader, error) {
|
||||
func (kc *KafkaConfiguration) NewReader(dialer *kafka.Dialer, logger *log.Entry) (*kafka.Reader, error) {
|
||||
rConf := kafka.ReaderConfig{
|
||||
Brokers: kc.Brokers,
|
||||
Topic: kc.Topic,
|
||||
Dialer: dialer,
|
||||
Brokers: kc.Brokers,
|
||||
Topic: kc.Topic,
|
||||
Dialer: dialer,
|
||||
Logger: kafka.LoggerFunc(logger.Debugf),
|
||||
ErrorLogger: kafka.LoggerFunc(logger.Errorf),
|
||||
}
|
||||
if kc.GroupID != "" && kc.Partition != 0 {
|
||||
return &kafka.Reader{}, fmt.Errorf("cannot specify both group_id and partition")
|
||||
}
|
||||
if kc.GroupID != "" {
|
||||
rConf.GroupID = kc.GroupID
|
||||
} else if kc.Partition != 0 {
|
||||
rConf.Partition = kc.Partition
|
||||
} else {
|
||||
logger.Warnf("no group_id specified, crowdsec will only read from the 1st partition of the topic")
|
||||
}
|
||||
if err := rConf.Validate(); err != nil {
|
||||
return &kafka.Reader{}, fmt.Errorf("while validating reader configuration: %w", err)
|
||||
|
|
|
@ -58,6 +58,16 @@ brokers:
|
|||
topic: crowdsec`,
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
config: `
|
||||
source: kafka
|
||||
brokers:
|
||||
- localhost:9092
|
||||
topic: crowdsec
|
||||
partition: 1
|
||||
group_id: crowdsec`,
|
||||
expectedErr: "cannote create kafka reader: cannot specify both group_id and partition",
|
||||
},
|
||||
}
|
||||
|
||||
subLogger := log.WithFields(log.Fields{
|
||||
|
|
60
pkg/acquisition/modules/loki/entry.go
Normal file
60
pkg/acquisition/modules/loki/entry.go
Normal file
|
@ -0,0 +1,60 @@
|
|||
package loki
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Entry struct {
|
||||
Timestamp time.Time
|
||||
Line string
|
||||
}
|
||||
|
||||
func (e *Entry) UnmarshalJSON(b []byte) error {
|
||||
var values []string
|
||||
err := json.Unmarshal(b, &values)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t, err := strconv.Atoi(values[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.Timestamp = time.Unix(int64(t), 0)
|
||||
e.Line = values[1]
|
||||
return nil
|
||||
}
|
||||
|
||||
type Stream struct {
|
||||
Stream map[string]string `json:"stream"`
|
||||
Entries []Entry `json:"values"`
|
||||
}
|
||||
|
||||
type DroppedEntry struct {
|
||||
Labels map[string]string `json:"labels"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
}
|
||||
|
||||
type Tail struct {
|
||||
Streams []Stream `json:"streams"`
|
||||
DroppedEntries []DroppedEntry `json:"dropped_entries"`
|
||||
}
|
||||
|
||||
// LokiQuery GET response.
|
||||
// See https://grafana.com/docs/loki/latest/api/#get-lokiapiv1query
|
||||
type LokiQuery struct {
|
||||
Status string `json:"status"`
|
||||
Data Data `json:"data"`
|
||||
}
|
||||
|
||||
type Data struct {
|
||||
ResultType string `json:"resultType"`
|
||||
Result []StreamResult `json:"result"` // Warning, just stream value is handled
|
||||
Stats interface{} `json:"stats"` // Stats is boring, just ignore it
|
||||
}
|
||||
|
||||
type StreamResult struct {
|
||||
Stream map[string]string `json:"stream"`
|
||||
Values []Entry `json:"values"`
|
||||
}
|
315
pkg/acquisition/modules/loki/internal/lokiclient/loki_client.go
Normal file
315
pkg/acquisition/modules/loki/internal/lokiclient/loki_client.go
Normal file
|
@ -0,0 +1,315 @@
|
|||
package lokiclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/cwversion"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gopkg.in/tomb.v2"
|
||||
)
|
||||
|
||||
type LokiClient struct {
|
||||
Logger *log.Entry
|
||||
|
||||
config Config
|
||||
t *tomb.Tomb
|
||||
fail_start time.Time
|
||||
currentTickerInterval time.Duration
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
LokiURL string
|
||||
LokiPrefix string
|
||||
Query string
|
||||
Headers map[string]string
|
||||
|
||||
Username string
|
||||
Password string
|
||||
|
||||
Since time.Duration
|
||||
Until time.Duration
|
||||
|
||||
FailMaxDuration time.Duration
|
||||
|
||||
DelayFor int
|
||||
Limit int
|
||||
}
|
||||
|
||||
func updateURI(uri string, lq LokiQueryRangeResponse, infinite bool) string {
|
||||
u, _ := url.Parse(uri)
|
||||
queryParams := u.Query()
|
||||
|
||||
if len(lq.Data.Result) > 0 {
|
||||
lastTs := lq.Data.Result[0].Entries[len(lq.Data.Result[0].Entries)-1].Timestamp
|
||||
// +1 the last timestamp to avoid getting the same result again.
|
||||
queryParams.Set("start", strconv.Itoa(int(lastTs.UnixNano()+1)))
|
||||
}
|
||||
|
||||
if infinite {
|
||||
queryParams.Set("end", strconv.Itoa(int(time.Now().UnixNano())))
|
||||
}
|
||||
|
||||
u.RawQuery = queryParams.Encode()
|
||||
return u.String()
|
||||
}
|
||||
|
||||
func (lc *LokiClient) SetTomb(t *tomb.Tomb) {
|
||||
lc.t = t
|
||||
}
|
||||
|
||||
func (lc *LokiClient) resetFailStart() {
|
||||
if !lc.fail_start.IsZero() {
|
||||
log.Infof("loki is back after %s", time.Since(lc.fail_start))
|
||||
}
|
||||
lc.fail_start = time.Time{}
|
||||
}
|
||||
func (lc *LokiClient) shouldRetry() bool {
|
||||
if lc.fail_start.IsZero() {
|
||||
lc.Logger.Warningf("loki is not available, will retry for %s", lc.config.FailMaxDuration)
|
||||
lc.fail_start = time.Now()
|
||||
return true
|
||||
}
|
||||
if time.Since(lc.fail_start) > lc.config.FailMaxDuration {
|
||||
lc.Logger.Errorf("loki didn't manage to recover after %s, giving up", lc.config.FailMaxDuration)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (lc *LokiClient) increaseTicker(ticker *time.Ticker) {
|
||||
maxTicker := 10 * time.Second
|
||||
if lc.currentTickerInterval < maxTicker {
|
||||
lc.currentTickerInterval *= 2
|
||||
if lc.currentTickerInterval > maxTicker {
|
||||
lc.currentTickerInterval = maxTicker
|
||||
}
|
||||
ticker.Reset(lc.currentTickerInterval)
|
||||
}
|
||||
}
|
||||
|
||||
func (lc *LokiClient) decreaseTicker(ticker *time.Ticker) {
|
||||
minTicker := 100 * time.Millisecond
|
||||
if lc.currentTickerInterval != minTicker {
|
||||
lc.currentTickerInterval = minTicker
|
||||
ticker.Reset(lc.currentTickerInterval)
|
||||
}
|
||||
}
|
||||
|
||||
func (lc *LokiClient) queryRange(uri string, ctx context.Context, c chan *LokiQueryRangeResponse, infinite bool) error {
|
||||
lc.currentTickerInterval = 100 * time.Millisecond
|
||||
ticker := time.NewTicker(lc.currentTickerInterval)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-lc.t.Dying():
|
||||
return lc.t.Err()
|
||||
case <-ticker.C:
|
||||
resp, err := http.Get(uri)
|
||||
if err != nil {
|
||||
if ok := lc.shouldRetry(); !ok {
|
||||
return errors.Wrapf(err, "error querying range")
|
||||
} else {
|
||||
lc.increaseTicker(ticker)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
if ok := lc.shouldRetry(); !ok {
|
||||
return errors.Wrapf(err, "bad HTTP response code: %d: %s", resp.StatusCode, string(body))
|
||||
} else {
|
||||
lc.increaseTicker(ticker)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
var lq LokiQueryRangeResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&lq); err != nil {
|
||||
resp.Body.Close()
|
||||
if ok := lc.shouldRetry(); !ok {
|
||||
return errors.Wrapf(err, "error decoding Loki response")
|
||||
} else {
|
||||
lc.increaseTicker(ticker)
|
||||
continue
|
||||
}
|
||||
}
|
||||
resp.Body.Close()
|
||||
lc.Logger.Tracef("Got response: %+v", lq)
|
||||
c <- &lq
|
||||
lc.resetFailStart()
|
||||
if !infinite && (len(lq.Data.Result) == 0 || len(lq.Data.Result[0].Entries) < lc.config.Limit) {
|
||||
lc.Logger.Infof("Got less than %d results (%d), stopping", lc.config.Limit, len(lq.Data.Result))
|
||||
close(c)
|
||||
return nil
|
||||
}
|
||||
if len(lq.Data.Result) > 0 {
|
||||
lc.Logger.Debugf("(timer:%v) %d results / %d entries result[0] (uri:%s)", lc.currentTickerInterval, len(lq.Data.Result), len(lq.Data.Result[0].Entries), uri)
|
||||
} else {
|
||||
lc.Logger.Debugf("(timer:%v) no results (uri:%s)", lc.currentTickerInterval, uri)
|
||||
}
|
||||
if infinite {
|
||||
if len(lq.Data.Result) > 0 { //as long as we get results, we keep lowest ticker
|
||||
lc.decreaseTicker(ticker)
|
||||
} else {
|
||||
lc.increaseTicker(ticker)
|
||||
}
|
||||
}
|
||||
|
||||
uri = updateURI(uri, lq, infinite)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (lc *LokiClient) getURLFor(endpoint string, params map[string]string) string {
|
||||
u, err := url.Parse(lc.config.LokiURL)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
queryParams := u.Query()
|
||||
for k, v := range params {
|
||||
queryParams.Set(k, v)
|
||||
}
|
||||
u.RawQuery = queryParams.Encode()
|
||||
|
||||
u.Path, err = url.JoinPath(lc.config.LokiPrefix, u.Path, endpoint)
|
||||
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if endpoint == "loki/api/v1/tail" {
|
||||
if u.Scheme == "http" {
|
||||
u.Scheme = "ws"
|
||||
} else {
|
||||
u.Scheme = "wss"
|
||||
}
|
||||
}
|
||||
|
||||
return u.String()
|
||||
}
|
||||
|
||||
func (lc *LokiClient) Ready(ctx context.Context) error {
|
||||
tick := time.NewTicker(500 * time.Millisecond)
|
||||
url := lc.getURLFor("ready", nil)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
tick.Stop()
|
||||
return ctx.Err()
|
||||
case <-lc.t.Dying():
|
||||
tick.Stop()
|
||||
return lc.t.Err()
|
||||
case <-tick.C:
|
||||
lc.Logger.Debug("Checking if Loki is ready")
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
lc.Logger.Warnf("Error checking if Loki is ready: %s", err)
|
||||
continue
|
||||
}
|
||||
_ = resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
lc.Logger.Debugf("Loki is not ready, status code: %d", resp.StatusCode)
|
||||
continue
|
||||
}
|
||||
lc.Logger.Info("Loki is ready")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (lc *LokiClient) Tail(ctx context.Context) (chan *LokiResponse, error) {
|
||||
responseChan := make(chan *LokiResponse)
|
||||
dialer := &websocket.Dialer{}
|
||||
u := lc.getURLFor("loki/api/v1/tail", map[string]string{
|
||||
"limit": strconv.Itoa(lc.config.Limit),
|
||||
"start": strconv.Itoa(int(time.Now().Add(-lc.config.Since).UnixNano())),
|
||||
"query": lc.config.Query,
|
||||
"delay_for": strconv.Itoa(lc.config.DelayFor),
|
||||
})
|
||||
|
||||
lc.Logger.Debugf("Since: %s (%s)", lc.config.Since, time.Now().Add(-lc.config.Since))
|
||||
|
||||
if lc.config.Username != "" || lc.config.Password != "" {
|
||||
dialer.Proxy = func(req *http.Request) (*url.URL, error) {
|
||||
req.SetBasicAuth(lc.config.Username, lc.config.Password)
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
requestHeader := http.Header{}
|
||||
for k, v := range lc.config.Headers {
|
||||
requestHeader.Add(k, v)
|
||||
}
|
||||
requestHeader.Set("User-Agent", "Crowdsec "+cwversion.VersionStr())
|
||||
lc.Logger.Infof("Connecting to %s", u)
|
||||
conn, _, err := dialer.Dial(u, requestHeader)
|
||||
|
||||
if err != nil {
|
||||
lc.Logger.Errorf("Error connecting to websocket, err: %s", err)
|
||||
return responseChan, fmt.Errorf("error connecting to websocket")
|
||||
}
|
||||
|
||||
lc.t.Go(func() error {
|
||||
for {
|
||||
jsonResponse := &LokiResponse{}
|
||||
err = conn.ReadJSON(jsonResponse)
|
||||
|
||||
if err != nil {
|
||||
lc.Logger.Errorf("Error reading from websocket: %s", err)
|
||||
return fmt.Errorf("websocket error: %w", err)
|
||||
}
|
||||
|
||||
responseChan <- jsonResponse
|
||||
}
|
||||
})
|
||||
|
||||
return responseChan, nil
|
||||
}
|
||||
|
||||
func (lc *LokiClient) QueryRange(ctx context.Context, infinite bool) chan *LokiQueryRangeResponse {
|
||||
url := lc.getURLFor("loki/api/v1/query_range", map[string]string{
|
||||
"query": lc.config.Query,
|
||||
"start": strconv.Itoa(int(time.Now().Add(-lc.config.Since).UnixNano())),
|
||||
"end": strconv.Itoa(int(time.Now().UnixNano())),
|
||||
"limit": strconv.Itoa(lc.config.Limit),
|
||||
"direction": "forward",
|
||||
})
|
||||
|
||||
c := make(chan *LokiQueryRangeResponse)
|
||||
|
||||
lc.Logger.Debugf("Since: %s (%s)", lc.config.Since, time.Now().Add(-lc.config.Since))
|
||||
|
||||
requestHeader := http.Header{}
|
||||
for k, v := range lc.config.Headers {
|
||||
requestHeader.Add(k, v)
|
||||
}
|
||||
|
||||
if lc.config.Username != "" || lc.config.Password != "" {
|
||||
requestHeader.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(lc.config.Username+":"+lc.config.Password)))
|
||||
}
|
||||
|
||||
requestHeader.Set("User-Agent", "Crowdsec "+cwversion.VersionStr())
|
||||
lc.Logger.Infof("Connecting to %s", url)
|
||||
lc.t.Go(func() error {
|
||||
return lc.queryRange(url, ctx, c, infinite)
|
||||
})
|
||||
return c
|
||||
}
|
||||
|
||||
func NewLokiClient(config Config) *LokiClient {
|
||||
return &LokiClient{Logger: log.WithField("component", "lokiclient"), config: config}
|
||||
}
|
55
pkg/acquisition/modules/loki/internal/lokiclient/types.go
Normal file
55
pkg/acquisition/modules/loki/internal/lokiclient/types.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
package lokiclient
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Entry struct {
|
||||
Timestamp time.Time
|
||||
Line string
|
||||
}
|
||||
|
||||
func (e *Entry) UnmarshalJSON(b []byte) error {
|
||||
var values []string
|
||||
err := json.Unmarshal(b, &values)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t, err := strconv.Atoi(values[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.Timestamp = time.Unix(0, int64(t))
|
||||
e.Line = values[1]
|
||||
return nil
|
||||
}
|
||||
|
||||
type Stream struct {
|
||||
Stream map[string]string `json:"stream"`
|
||||
Entries []Entry `json:"values"`
|
||||
}
|
||||
|
||||
type DroppedEntry struct {
|
||||
Labels map[string]string `json:"labels"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
}
|
||||
|
||||
type LokiResponse struct {
|
||||
Streams []Stream `json:"streams"`
|
||||
DroppedEntries []interface{} `json:"dropped_entries"` //We don't care about the actual content i think ?
|
||||
}
|
||||
|
||||
// LokiQuery GET response.
|
||||
// See https://grafana.com/docs/loki/latest/api/#get-lokiapiv1query
|
||||
type LokiQueryRangeResponse struct {
|
||||
Status string `json:"status"`
|
||||
Data Data `json:"data"`
|
||||
}
|
||||
|
||||
type Data struct {
|
||||
ResultType string `json:"resultType"`
|
||||
Result []Stream `json:"result"` // Warning, just stream value is handled
|
||||
Stats interface{} `json:"stats"` // Stats is boring, just ignore it
|
||||
}
|
370
pkg/acquisition/modules/loki/loki.go
Normal file
370
pkg/acquisition/modules/loki/loki.go
Normal file
|
@ -0,0 +1,370 @@
|
|||
package loki
|
||||
|
||||
/*
|
||||
https://grafana.com/docs/loki/latest/api/#get-lokiapiv1tail
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
tomb "gopkg.in/tomb.v2"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/acquisition/configuration"
|
||||
lokiclient "github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/loki/internal/lokiclient"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
)
|
||||
|
||||
const (
|
||||
readyTimeout time.Duration = 3 * time.Second
|
||||
readyLoop int = 3
|
||||
readySleep time.Duration = 10 * time.Second
|
||||
lokiLimit int = 100
|
||||
)
|
||||
|
||||
var linesRead = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "cs_lokisource_hits_total",
|
||||
Help: "Total lines that were read.",
|
||||
},
|
||||
[]string{"source"})
|
||||
|
||||
type LokiAuthConfiguration struct {
|
||||
Username string `yaml:"username"`
|
||||
Password string `yaml:"password"`
|
||||
}
|
||||
|
||||
type LokiConfiguration struct {
|
||||
URL string `yaml:"url"` // Loki url
|
||||
Prefix string `yaml:"prefix"` // Loki prefix
|
||||
Query string `yaml:"query"` // LogQL query
|
||||
Limit int `yaml:"limit"` // Limit of logs to read
|
||||
DelayFor time.Duration `yaml:"delay_for"`
|
||||
Since time.Duration `yaml:"since"`
|
||||
Headers map[string]string `yaml:"headers"` // HTTP headers for talking to Loki
|
||||
WaitForReady time.Duration `yaml:"wait_for_ready"` // Retry interval, default is 10 seconds
|
||||
Auth LokiAuthConfiguration `yaml:"auth"`
|
||||
MaxFailureDuration time.Duration `yaml:"max_failure_duration"` // Max duration of failure before stopping the source
|
||||
configuration.DataSourceCommonCfg `yaml:",inline"`
|
||||
}
|
||||
|
||||
type LokiSource struct {
|
||||
Config LokiConfiguration
|
||||
|
||||
Client *lokiclient.LokiClient
|
||||
|
||||
logger *log.Entry
|
||||
lokiWebsocket string
|
||||
}
|
||||
|
||||
func (l *LokiSource) GetMetrics() []prometheus.Collector {
|
||||
return []prometheus.Collector{linesRead}
|
||||
}
|
||||
|
||||
func (l *LokiSource) GetAggregMetrics() []prometheus.Collector {
|
||||
return []prometheus.Collector{linesRead}
|
||||
}
|
||||
|
||||
func (l *LokiSource) UnmarshalConfig(yamlConfig []byte) error {
|
||||
err := yaml.UnmarshalStrict(yamlConfig, &l.Config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot parse loki acquisition configuration: %w", err)
|
||||
}
|
||||
|
||||
if l.Config.Query == "" {
|
||||
return errors.New("loki query is mandatory")
|
||||
}
|
||||
|
||||
if l.Config.WaitForReady == 0 {
|
||||
l.Config.WaitForReady = 10 * time.Second
|
||||
}
|
||||
|
||||
if l.Config.DelayFor < 0*time.Second || l.Config.DelayFor > 5*time.Second {
|
||||
return errors.New("delay_for should be a value between 1s and 5s")
|
||||
}
|
||||
|
||||
if l.Config.Mode == "" {
|
||||
l.Config.Mode = configuration.TAIL_MODE
|
||||
}
|
||||
if l.Config.Prefix == "" {
|
||||
l.Config.Prefix = "/"
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(l.Config.Prefix, "/") {
|
||||
l.Config.Prefix += "/"
|
||||
}
|
||||
|
||||
if l.Config.Limit == 0 {
|
||||
l.Config.Limit = lokiLimit
|
||||
}
|
||||
|
||||
if l.Config.Mode == configuration.TAIL_MODE {
|
||||
l.logger.Infof("Resetting since")
|
||||
l.Config.Since = 0
|
||||
}
|
||||
|
||||
if l.Config.MaxFailureDuration == 0 {
|
||||
l.Config.MaxFailureDuration = 30 * time.Second
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LokiSource) Configure(config []byte, logger *log.Entry) error {
|
||||
l.Config = LokiConfiguration{}
|
||||
l.logger = logger
|
||||
err := l.UnmarshalConfig(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l.logger.Infof("Since value: %s", l.Config.Since.String())
|
||||
|
||||
clientConfig := lokiclient.Config{
|
||||
LokiURL: l.Config.URL,
|
||||
Headers: l.Config.Headers,
|
||||
Limit: l.Config.Limit,
|
||||
Query: l.Config.Query,
|
||||
Since: l.Config.Since,
|
||||
Username: l.Config.Auth.Username,
|
||||
Password: l.Config.Auth.Password,
|
||||
FailMaxDuration: l.Config.MaxFailureDuration,
|
||||
}
|
||||
|
||||
l.Client = lokiclient.NewLokiClient(clientConfig)
|
||||
l.Client.Logger = logger.WithFields(log.Fields{"component": "lokiclient", "source": l.Config.URL})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LokiSource) ConfigureByDSN(dsn string, labels map[string]string, logger *log.Entry, uuid string) error {
|
||||
l.logger = logger
|
||||
l.Config = LokiConfiguration{}
|
||||
l.Config.Mode = configuration.CAT_MODE
|
||||
l.Config.Labels = labels
|
||||
l.Config.UniqueId = uuid
|
||||
|
||||
u, err := url.Parse(dsn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("while parsing dsn '%s': %w", dsn, err)
|
||||
}
|
||||
if u.Scheme != "loki" {
|
||||
return fmt.Errorf("invalid DSN %s for loki source, must start with loki://", dsn)
|
||||
}
|
||||
if u.Host == "" {
|
||||
return errors.New("empty loki host")
|
||||
}
|
||||
scheme := "http"
|
||||
|
||||
params := u.Query()
|
||||
if q := params.Get("ssl"); q != "" {
|
||||
scheme = "https"
|
||||
}
|
||||
if q := params.Get("query"); q != "" {
|
||||
l.Config.Query = q
|
||||
}
|
||||
if w := params.Get("wait_for_ready"); w != "" {
|
||||
l.Config.WaitForReady, err = time.ParseDuration(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
l.Config.WaitForReady = 10 * time.Second
|
||||
}
|
||||
|
||||
if d := params.Get("delay_for"); d != "" {
|
||||
l.Config.DelayFor, err = time.ParseDuration(d)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid duration: %w", err)
|
||||
}
|
||||
if l.Config.DelayFor < 0*time.Second || l.Config.DelayFor > 5*time.Second {
|
||||
return errors.New("delay_for should be a value between 1s and 5s")
|
||||
}
|
||||
} else {
|
||||
l.Config.DelayFor = 0 * time.Second
|
||||
}
|
||||
|
||||
if s := params.Get("since"); s != "" {
|
||||
l.Config.Since, err = time.ParseDuration(s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid since in dsn: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if max_failure_duration := params.Get("max_failure_duration"); max_failure_duration != "" {
|
||||
duration, err := time.ParseDuration(max_failure_duration)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid max_failure_duration in dsn: %w", err)
|
||||
}
|
||||
l.Config.MaxFailureDuration = duration
|
||||
} else {
|
||||
l.Config.MaxFailureDuration = 5 * time.Second // for OneShot mode it doesn't make sense to have longer duration
|
||||
}
|
||||
|
||||
if limit := params.Get("limit"); limit != "" {
|
||||
limit, err := strconv.Atoi(limit)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid limit in dsn: %w", err)
|
||||
}
|
||||
l.Config.Limit = limit
|
||||
} else {
|
||||
l.Config.Limit = 5000 // max limit allowed by loki
|
||||
}
|
||||
|
||||
if logLevel := params.Get("log_level"); logLevel != "" {
|
||||
level, err := log.ParseLevel(logLevel)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid log_level in dsn: %w", err)
|
||||
}
|
||||
l.Config.LogLevel = &level
|
||||
l.logger.Logger.SetLevel(level)
|
||||
}
|
||||
|
||||
l.Config.URL = fmt.Sprintf("%s://%s", scheme, u.Host)
|
||||
if u.User != nil {
|
||||
l.Config.Auth.Username = u.User.Username()
|
||||
l.Config.Auth.Password, _ = u.User.Password()
|
||||
}
|
||||
|
||||
clientConfig := lokiclient.Config{
|
||||
LokiURL: l.Config.URL,
|
||||
Headers: l.Config.Headers,
|
||||
Limit: l.Config.Limit,
|
||||
Query: l.Config.Query,
|
||||
Since: l.Config.Since,
|
||||
Username: l.Config.Auth.Username,
|
||||
Password: l.Config.Auth.Password,
|
||||
DelayFor: int(l.Config.DelayFor / time.Second),
|
||||
}
|
||||
|
||||
l.Client = lokiclient.NewLokiClient(clientConfig)
|
||||
l.Client.Logger = logger.WithFields(log.Fields{"component": "lokiclient", "source": l.Config.URL})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LokiSource) GetMode() string {
|
||||
return l.Config.Mode
|
||||
}
|
||||
|
||||
func (l *LokiSource) GetName() string {
|
||||
return "loki"
|
||||
}
|
||||
|
||||
// OneShotAcquisition reads a set of file and returns when done
|
||||
func (l *LokiSource) OneShotAcquisition(out chan types.Event, t *tomb.Tomb) error {
|
||||
l.logger.Debug("Loki one shot acquisition")
|
||||
l.Client.SetTomb(t)
|
||||
readyCtx, cancel := context.WithTimeout(context.Background(), l.Config.WaitForReady)
|
||||
defer cancel()
|
||||
err := l.Client.Ready(readyCtx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("loki is not ready: %w", err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
c := l.Client.QueryRange(ctx, false)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-t.Dying():
|
||||
l.logger.Debug("Loki one shot acquisition stopped")
|
||||
cancel()
|
||||
return nil
|
||||
case resp, ok := <-c:
|
||||
if !ok {
|
||||
l.logger.Info("Loki acquisition done, chan closed")
|
||||
cancel()
|
||||
return nil
|
||||
}
|
||||
for _, stream := range resp.Data.Result {
|
||||
for _, entry := range stream.Entries {
|
||||
l.readOneEntry(entry, l.Config.Labels, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LokiSource) readOneEntry(entry lokiclient.Entry, labels map[string]string, out chan types.Event) {
|
||||
ll := types.Line{}
|
||||
ll.Raw = entry.Line
|
||||
ll.Time = entry.Timestamp
|
||||
ll.Src = l.Config.URL
|
||||
ll.Labels = labels
|
||||
ll.Process = true
|
||||
ll.Module = l.GetName()
|
||||
|
||||
linesRead.With(prometheus.Labels{"source": l.Config.URL}).Inc()
|
||||
expectMode := types.LIVE
|
||||
if l.Config.UseTimeMachine {
|
||||
expectMode = types.TIMEMACHINE
|
||||
}
|
||||
out <- types.Event{
|
||||
Line: ll,
|
||||
Process: true,
|
||||
Type: types.LOG,
|
||||
ExpectMode: expectMode,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LokiSource) StreamingAcquisition(out chan types.Event, t *tomb.Tomb) error {
|
||||
l.Client.SetTomb(t)
|
||||
readyCtx, cancel := context.WithTimeout(context.Background(), l.Config.WaitForReady)
|
||||
defer cancel()
|
||||
err := l.Client.Ready(readyCtx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("loki is not ready: %w", err)
|
||||
}
|
||||
ll := l.logger.WithField("websocket_url", l.lokiWebsocket)
|
||||
t.Go(func() error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
respChan := l.Client.QueryRange(ctx, true)
|
||||
if err != nil {
|
||||
ll.Errorf("could not start loki tail: %s", err)
|
||||
return fmt.Errorf("while starting loki tail: %w", err)
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case resp, ok := <-respChan:
|
||||
if !ok {
|
||||
ll.Warnf("loki channel closed")
|
||||
return err
|
||||
}
|
||||
for _, stream := range resp.Data.Result {
|
||||
for _, entry := range stream.Entries {
|
||||
l.readOneEntry(entry, l.Config.Labels, out)
|
||||
}
|
||||
}
|
||||
case <-t.Dying():
|
||||
return nil
|
||||
}
|
||||
}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LokiSource) CanRun() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LokiSource) GetUuid() string {
|
||||
return l.Config.UniqueId
|
||||
}
|
||||
|
||||
func (l *LokiSource) Dump() interface{} {
|
||||
return l
|
||||
}
|
||||
|
||||
// SupportedModes returns the supported modes by the acquisition module
|
||||
func (l *LokiSource) SupportedModes() []string {
|
||||
return []string{configuration.TAIL_MODE, configuration.CAT_MODE}
|
||||
}
|
512
pkg/acquisition/modules/loki/loki_test.go
Normal file
512
pkg/acquisition/modules/loki/loki_test.go
Normal file
|
@ -0,0 +1,512 @@
|
|||
package loki_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"context"
|
||||
|
||||
"github.com/crowdsecurity/go-cs-lib/cstest"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/loki"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
log "github.com/sirupsen/logrus"
|
||||
tomb "gopkg.in/tomb.v2"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestConfiguration(t *testing.T) {
|
||||
|
||||
log.Infof("Test 'TestConfigure'")
|
||||
|
||||
tests := []struct {
|
||||
config string
|
||||
expectedErr string
|
||||
password string
|
||||
waitForReady time.Duration
|
||||
delayFor time.Duration
|
||||
testName string
|
||||
}{
|
||||
{
|
||||
config: `foobar: asd`,
|
||||
expectedErr: "line 1: field foobar not found in type loki.LokiConfiguration",
|
||||
testName: "Unknown field",
|
||||
},
|
||||
{
|
||||
config: `
|
||||
mode: tail
|
||||
source: loki`,
|
||||
expectedErr: "loki query is mandatory",
|
||||
testName: "Missing url",
|
||||
},
|
||||
{
|
||||
config: `
|
||||
mode: tail
|
||||
source: loki
|
||||
url: http://localhost:3100/
|
||||
`,
|
||||
expectedErr: "loki query is mandatory",
|
||||
testName: "Missing query",
|
||||
},
|
||||
{
|
||||
config: `
|
||||
mode: tail
|
||||
source: loki
|
||||
url: http://localhost:3100/
|
||||
query: >
|
||||
{server="demo"}
|
||||
`,
|
||||
expectedErr: "",
|
||||
testName: "Correct config",
|
||||
},
|
||||
{
|
||||
config: `
|
||||
mode: tail
|
||||
source: loki
|
||||
url: http://localhost:3100/
|
||||
wait_for_ready: 5s
|
||||
query: >
|
||||
{server="demo"}
|
||||
`,
|
||||
expectedErr: "",
|
||||
testName: "Correct config with wait_for_ready",
|
||||
waitForReady: 5 * time.Second,
|
||||
},
|
||||
{
|
||||
config: `
|
||||
mode: tail
|
||||
source: loki
|
||||
url: http://localhost:3100/
|
||||
delay_for: 1s
|
||||
query: >
|
||||
{server="demo"}
|
||||
`,
|
||||
expectedErr: "",
|
||||
testName: "Correct config with delay_for",
|
||||
delayFor: 1 * time.Second,
|
||||
},
|
||||
{
|
||||
|
||||
config: `
|
||||
mode: tail
|
||||
source: loki
|
||||
url: http://localhost:3100/
|
||||
auth:
|
||||
username: foo
|
||||
password: bar
|
||||
query: >
|
||||
{server="demo"}
|
||||
`,
|
||||
expectedErr: "",
|
||||
password: "bar",
|
||||
testName: "Correct config with password",
|
||||
},
|
||||
{
|
||||
|
||||
config: `
|
||||
mode: tail
|
||||
source: loki
|
||||
url: http://localhost:3100/
|
||||
delay_for: 10s
|
||||
query: >
|
||||
{server="demo"}
|
||||
`,
|
||||
expectedErr: "delay_for should be a value between 1s and 5s",
|
||||
testName: "Invalid DelayFor",
|
||||
},
|
||||
}
|
||||
subLogger := log.WithFields(log.Fields{
|
||||
"type": "loki",
|
||||
})
|
||||
for _, test := range tests {
|
||||
t.Run(test.testName, func(t *testing.T) {
|
||||
lokiSource := loki.LokiSource{}
|
||||
err := lokiSource.Configure([]byte(test.config), subLogger)
|
||||
cstest.AssertErrorContains(t, err, test.expectedErr)
|
||||
if test.password != "" {
|
||||
p := lokiSource.Config.Auth.Password
|
||||
if test.password != p {
|
||||
t.Fatalf("Password mismatch : %s != %s", test.password, p)
|
||||
}
|
||||
}
|
||||
if test.waitForReady != 0 {
|
||||
if lokiSource.Config.WaitForReady != test.waitForReady {
|
||||
t.Fatalf("Wrong WaitForReady %v != %v", lokiSource.Config.WaitForReady, test.waitForReady)
|
||||
}
|
||||
}
|
||||
if test.delayFor != 0 {
|
||||
if lokiSource.Config.DelayFor != test.delayFor {
|
||||
t.Fatalf("Wrong DelayFor %v != %v", lokiSource.Config.DelayFor, test.delayFor)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigureDSN(t *testing.T) {
|
||||
log.Infof("Test 'TestConfigureDSN'")
|
||||
tests := []struct {
|
||||
name string
|
||||
dsn string
|
||||
expectedErr string
|
||||
since time.Time
|
||||
password string
|
||||
scheme string
|
||||
waitForReady time.Duration
|
||||
delayFor time.Duration
|
||||
}{
|
||||
{
|
||||
name: "Wrong scheme",
|
||||
dsn: "wrong://",
|
||||
expectedErr: "invalid DSN wrong:// for loki source, must start with loki://",
|
||||
},
|
||||
{
|
||||
name: "Correct DSN",
|
||||
dsn: `loki://localhost:3100/?query={server="demo"}`,
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
name: "Empty host",
|
||||
dsn: "loki://",
|
||||
expectedErr: "empty loki host",
|
||||
},
|
||||
{
|
||||
name: "Invalid DSN",
|
||||
dsn: "loki",
|
||||
expectedErr: "invalid DSN loki for loki source, must start with loki://",
|
||||
},
|
||||
{
|
||||
name: "Invalid Delay",
|
||||
dsn: `loki://localhost:3100/?query={server="demo"}&delay_for=10s`,
|
||||
expectedErr: "delay_for should be a value between 1s and 5s",
|
||||
},
|
||||
{
|
||||
name: "Bad since param",
|
||||
dsn: `loki://127.0.0.1:3100/?since=3h&query={server="demo"}`,
|
||||
since: time.Now().Add(-3 * time.Hour),
|
||||
},
|
||||
{
|
||||
name: "Basic Auth",
|
||||
dsn: `loki://login:password@localhost:3102/?query={server="demo"}`,
|
||||
password: "password",
|
||||
},
|
||||
{
|
||||
name: "Correct DSN",
|
||||
dsn: `loki://localhost:3100/?query={server="demo"}&wait_for_ready=5s&delay_for=1s`,
|
||||
expectedErr: "",
|
||||
waitForReady: 5 * time.Second,
|
||||
delayFor: 1 * time.Second,
|
||||
},
|
||||
{
|
||||
name: "SSL DSN",
|
||||
dsn: `loki://localhost:3100/?ssl=true`,
|
||||
scheme: "https",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
subLogger := log.WithFields(log.Fields{
|
||||
"type": "loki",
|
||||
"name": test.name,
|
||||
})
|
||||
t.Logf("Test : %s", test.name)
|
||||
lokiSource := &loki.LokiSource{}
|
||||
err := lokiSource.ConfigureByDSN(test.dsn, map[string]string{"type": "testtype"}, subLogger, "")
|
||||
cstest.AssertErrorContains(t, err, test.expectedErr)
|
||||
|
||||
noDuration, _ := time.ParseDuration("0s")
|
||||
if lokiSource.Config.Since != noDuration && lokiSource.Config.Since.Round(time.Second) != time.Since(test.since).Round(time.Second) {
|
||||
t.Fatalf("Invalid since %v", lokiSource.Config.Since)
|
||||
}
|
||||
|
||||
if test.password != "" {
|
||||
p := lokiSource.Config.Auth.Password
|
||||
if test.password != p {
|
||||
t.Fatalf("Password mismatch : %s != %s", test.password, p)
|
||||
}
|
||||
}
|
||||
if test.scheme != "" {
|
||||
url, _ := url.Parse(lokiSource.Config.URL)
|
||||
if test.scheme != url.Scheme {
|
||||
t.Fatalf("Schema mismatch : %s != %s", test.scheme, url.Scheme)
|
||||
}
|
||||
}
|
||||
if test.waitForReady != 0 {
|
||||
if lokiSource.Config.WaitForReady != test.waitForReady {
|
||||
t.Fatalf("Wrong WaitForReady %v != %v", lokiSource.Config.WaitForReady, test.waitForReady)
|
||||
}
|
||||
}
|
||||
if test.delayFor != 0 {
|
||||
if lokiSource.Config.DelayFor != test.delayFor {
|
||||
t.Fatalf("Wrong DelayFor %v != %v", lokiSource.Config.DelayFor, test.delayFor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func feedLoki(logger *log.Entry, n int, title string) error {
|
||||
streams := LogStreams{
|
||||
Streams: []LogStream{
|
||||
{
|
||||
Stream: map[string]string{
|
||||
"server": "demo",
|
||||
"domain": "cw.example.com",
|
||||
"key": title,
|
||||
},
|
||||
Values: make([]LogValue, n),
|
||||
},
|
||||
},
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
streams.Streams[0].Values[i] = LogValue{
|
||||
Time: time.Now(),
|
||||
Line: fmt.Sprintf("Log line #%d %v", i, title),
|
||||
}
|
||||
}
|
||||
buff, err := json.Marshal(streams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp, err := http.Post("http://127.0.0.1:3100/loki/api/v1/push", "application/json", bytes.NewBuffer(buff))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode != http.StatusNoContent {
|
||||
b, _ := io.ReadAll(resp.Body)
|
||||
logger.Error(string(b))
|
||||
return fmt.Errorf("Bad post status %d", resp.StatusCode)
|
||||
}
|
||||
logger.Info(n, " Events sent")
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestOneShotAcquisition(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Skipping test on windows")
|
||||
}
|
||||
log.SetOutput(os.Stdout)
|
||||
log.SetLevel(log.InfoLevel)
|
||||
log.Info("Test 'TestStreamingAcquisition'")
|
||||
title := time.Now().String() // Loki will be messy, with a lot of stuff, lets use a unique key
|
||||
tests := []struct {
|
||||
config string
|
||||
}{
|
||||
{
|
||||
config: fmt.Sprintf(`
|
||||
mode: cat
|
||||
source: loki
|
||||
url: http://127.0.0.1:3100
|
||||
query: '{server="demo",key="%s"}'
|
||||
since: 1h
|
||||
`, title),
|
||||
},
|
||||
}
|
||||
|
||||
for _, ts := range tests {
|
||||
logger := log.New()
|
||||
subLogger := logger.WithFields(log.Fields{
|
||||
"type": "loki",
|
||||
})
|
||||
lokiSource := loki.LokiSource{}
|
||||
err := lokiSource.Configure([]byte(ts.config), subLogger)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error : %s", err)
|
||||
}
|
||||
|
||||
err = feedLoki(subLogger, 20, title)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error : %s", err)
|
||||
}
|
||||
|
||||
out := make(chan types.Event)
|
||||
read := 0
|
||||
go func() {
|
||||
for {
|
||||
<-out
|
||||
read++
|
||||
}
|
||||
}()
|
||||
lokiTomb := tomb.Tomb{}
|
||||
err = lokiSource.OneShotAcquisition(out, &lokiTomb)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error : %s", err)
|
||||
}
|
||||
assert.Equal(t, 20, read)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestStreamingAcquisition(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Skipping test on windows")
|
||||
}
|
||||
log.SetOutput(os.Stdout)
|
||||
log.SetLevel(log.InfoLevel)
|
||||
log.Info("Test 'TestStreamingAcquisition'")
|
||||
title := time.Now().String()
|
||||
tests := []struct {
|
||||
name string
|
||||
config string
|
||||
expectedErr string
|
||||
streamErr string
|
||||
expectedLines int
|
||||
}{
|
||||
{
|
||||
name: "Bad port",
|
||||
config: `
|
||||
mode: tail
|
||||
source: loki
|
||||
url: http://127.0.0.1:3101
|
||||
query: >
|
||||
{server="demo"}
|
||||
`, // No Loki server here
|
||||
expectedErr: "",
|
||||
streamErr: `loki is not ready: context deadline exceeded`,
|
||||
expectedLines: 0,
|
||||
},
|
||||
{
|
||||
name: "ok",
|
||||
config: `
|
||||
mode: tail
|
||||
source: loki
|
||||
url: http://127.0.0.1:3100
|
||||
query: >
|
||||
{server="demo"}
|
||||
`,
|
||||
expectedErr: "",
|
||||
streamErr: "",
|
||||
expectedLines: 20,
|
||||
},
|
||||
}
|
||||
for _, ts := range tests {
|
||||
t.Run(ts.name, func(t *testing.T) {
|
||||
logger := log.New()
|
||||
subLogger := logger.WithFields(log.Fields{
|
||||
"type": "loki",
|
||||
"name": ts.name,
|
||||
})
|
||||
|
||||
out := make(chan types.Event)
|
||||
lokiTomb := tomb.Tomb{}
|
||||
lokiSource := loki.LokiSource{}
|
||||
err := lokiSource.Configure([]byte(ts.config), subLogger)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error : %s", err)
|
||||
}
|
||||
err = lokiSource.StreamingAcquisition(out, &lokiTomb)
|
||||
cstest.AssertErrorContains(t, err, ts.streamErr)
|
||||
|
||||
if ts.streamErr != "" {
|
||||
return
|
||||
}
|
||||
|
||||
time.Sleep(time.Second * 2) //We need to give time to start reading from the WS
|
||||
readTomb := tomb.Tomb{}
|
||||
readCtx, cancel := context.WithTimeout(context.Background(), time.Second*10)
|
||||
count := 0
|
||||
|
||||
readTomb.Go(func() error {
|
||||
defer cancel()
|
||||
for {
|
||||
select {
|
||||
case <-readCtx.Done():
|
||||
return readCtx.Err()
|
||||
case evt := <-out:
|
||||
count++
|
||||
if !strings.HasSuffix(evt.Line.Raw, title) {
|
||||
return fmt.Errorf("Incorrect suffix : %s", evt.Line.Raw)
|
||||
}
|
||||
if count == ts.expectedLines {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
err = feedLoki(subLogger, ts.expectedLines, title)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error : %s", err)
|
||||
}
|
||||
|
||||
err = readTomb.Wait()
|
||||
cancel()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error : %s", err)
|
||||
}
|
||||
assert.Equal(t, count, ts.expectedLines)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestStopStreaming(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Skipping test on windows")
|
||||
}
|
||||
config := `
|
||||
mode: tail
|
||||
source: loki
|
||||
url: http://127.0.0.1:3100
|
||||
query: >
|
||||
{server="demo"}
|
||||
`
|
||||
logger := log.New()
|
||||
subLogger := logger.WithFields(log.Fields{
|
||||
"type": "loki",
|
||||
})
|
||||
title := time.Now().String()
|
||||
lokiSource := loki.LokiSource{}
|
||||
err := lokiSource.Configure([]byte(config), subLogger)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error : %s", err)
|
||||
}
|
||||
out := make(chan types.Event)
|
||||
|
||||
lokiTomb := &tomb.Tomb{}
|
||||
err = lokiSource.StreamingAcquisition(out, lokiTomb)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error : %s", err)
|
||||
}
|
||||
time.Sleep(time.Second * 2)
|
||||
err = feedLoki(subLogger, 1, title)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error : %s", err)
|
||||
}
|
||||
|
||||
lokiTomb.Kill(nil)
|
||||
err = lokiTomb.Wait()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error : %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
type LogStreams struct {
|
||||
Streams []LogStream `json:"streams"`
|
||||
}
|
||||
|
||||
type LogStream struct {
|
||||
Stream map[string]string `json:"stream"`
|
||||
Values []LogValue `json:"values"`
|
||||
}
|
||||
|
||||
type LogValue struct {
|
||||
Time time.Time
|
||||
Line string
|
||||
}
|
||||
|
||||
func (l *LogValue) MarshalJSON() ([]byte, error) {
|
||||
line, err := json.Marshal(l.Line)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte(fmt.Sprintf(`["%d",%s]`, l.Time.UnixNano(), string(line))), nil
|
||||
}
|
29
pkg/acquisition/modules/loki/timestamp.go
Normal file
29
pkg/acquisition/modules/loki/timestamp.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package loki
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type timestamp time.Time
|
||||
|
||||
func (t *timestamp) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var tt time.Time
|
||||
err := unmarshal(&tt)
|
||||
if err == nil {
|
||||
*t = timestamp(tt)
|
||||
return nil
|
||||
}
|
||||
var d time.Duration
|
||||
err = unmarshal(&d)
|
||||
if err == nil {
|
||||
*t = timestamp(time.Now().Add(-d))
|
||||
fmt.Println("t", time.Time(*t).Format(time.RFC3339))
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *timestamp) IsZero() bool {
|
||||
return time.Time(*t).IsZero()
|
||||
}
|
47
pkg/acquisition/modules/loki/timestamp_test.go
Normal file
47
pkg/acquisition/modules/loki/timestamp_test.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
package loki
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func TestTimestampFail(t *testing.T) {
|
||||
var tt timestamp
|
||||
err := yaml.Unmarshal([]byte("plop"), tt)
|
||||
if err == nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimestampTime(t *testing.T) {
|
||||
var tt timestamp
|
||||
const ts string = "2022-06-14T12:56:39+02:00"
|
||||
err := yaml.Unmarshal([]byte(ts), &tt)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.Fail()
|
||||
}
|
||||
if ts != time.Time(tt).Format(time.RFC3339) {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimestampDuration(t *testing.T) {
|
||||
var tt timestamp
|
||||
err := yaml.Unmarshal([]byte("3h"), &tt)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.Fail()
|
||||
}
|
||||
d, err := time.ParseDuration("3h")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.Fail()
|
||||
}
|
||||
z := time.Now().Add(-d)
|
||||
if z.Round(time.Second) != time.Time(tt).Round(time.Second) {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
|
@ -211,8 +211,10 @@ func (s *SyslogSource) handleSyslogMsg(out chan types.Event, t *tomb.Tomb, c cha
|
|||
continue
|
||||
}
|
||||
line = s.buildLogFromSyslog(p2.Timestamp, p2.Hostname, p2.Tag, p2.PID, p2.Message)
|
||||
linesParsed.With(prometheus.Labels{"source": syslogLine.Client, "type": "rfc5424"}).Inc()
|
||||
} else {
|
||||
line = s.buildLogFromSyslog(p.Timestamp, p.Hostname, p.Tag, p.PID, p.Message)
|
||||
linesParsed.With(prometheus.Labels{"source": syslogLine.Client, "type": "rfc3164"}).Inc()
|
||||
}
|
||||
|
||||
line = strings.TrimSuffix(line, "\n")
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -17,6 +16,7 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gopkg.in/tomb.v2"
|
||||
"slices"
|
||||
|
||||
"github.com/crowdsecurity/go-cs-lib/ptr"
|
||||
"github.com/crowdsecurity/go-cs-lib/trace"
|
||||
|
@ -383,19 +383,16 @@ func (a *apic) CAPIPullIsOld() (bool, error) {
|
|||
}
|
||||
|
||||
func (a *apic) HandleDeletedDecisions(deletedDecisions []*models.Decision, delete_counters map[string]map[string]int) (int, error) {
|
||||
var filter map[string][]string
|
||||
var nbDeleted int
|
||||
nbDeleted := 0
|
||||
for _, decision := range deletedDecisions {
|
||||
if strings.ToLower(*decision.Scope) == "ip" {
|
||||
filter = make(map[string][]string, 1)
|
||||
filter["value"] = []string{*decision.Value}
|
||||
} else {
|
||||
filter = make(map[string][]string, 3)
|
||||
filter["value"] = []string{*decision.Value}
|
||||
filter := map[string][]string{
|
||||
"value": {*decision.Value},
|
||||
"origin": {*decision.Origin},
|
||||
}
|
||||
if strings.ToLower(*decision.Scope) != "ip" {
|
||||
filter["type"] = []string{*decision.Type}
|
||||
filter["scopes"] = []string{*decision.Scope}
|
||||
}
|
||||
filter["origin"] = []string{*decision.Origin}
|
||||
|
||||
dbCliRet, _, err := a.dbClient.SoftDeleteDecisionsWithFilter(filter)
|
||||
if err != nil {
|
||||
|
@ -412,20 +409,17 @@ func (a *apic) HandleDeletedDecisions(deletedDecisions []*models.Decision, delet
|
|||
}
|
||||
|
||||
func (a *apic) HandleDeletedDecisionsV3(deletedDecisions []*modelscapi.GetDecisionsStreamResponseDeletedItem, delete_counters map[string]map[string]int) (int, error) {
|
||||
var filter map[string][]string
|
||||
var nbDeleted int
|
||||
for _, decisions := range deletedDecisions {
|
||||
scope := decisions.Scope
|
||||
for _, decision := range decisions.Decisions {
|
||||
if strings.ToLower(*scope) == "ip" {
|
||||
filter = make(map[string][]string, 1)
|
||||
filter["value"] = []string{decision}
|
||||
} else {
|
||||
filter = make(map[string][]string, 2)
|
||||
filter["value"] = []string{decision}
|
||||
filter := map[string][]string{
|
||||
"value": {decision},
|
||||
"origin": {types.CAPIOrigin},
|
||||
}
|
||||
if strings.ToLower(*scope) != "ip" {
|
||||
filter["scopes"] = []string{*scope}
|
||||
}
|
||||
filter["origin"] = []string{types.CAPIOrigin}
|
||||
|
||||
dbCliRet, _, err := a.dbClient.SoftDeleteDecisionsWithFilter(filter)
|
||||
if err != nil {
|
||||
|
@ -479,30 +473,42 @@ func createAlertsForDecisions(decisions []*models.Decision) []*models.Alert {
|
|||
}
|
||||
|
||||
func createAlertForDecision(decision *models.Decision) *models.Alert {
|
||||
newAlert := &models.Alert{}
|
||||
newAlert.Source = &models.Source{}
|
||||
newAlert.Source.Scope = ptr.Of("")
|
||||
if *decision.Origin == types.CAPIOrigin { //to make things more user friendly, we replace CAPI with community-blocklist
|
||||
newAlert.Scenario = ptr.Of(types.CAPIOrigin)
|
||||
newAlert.Source.Scope = ptr.Of(types.CAPIOrigin)
|
||||
} else if *decision.Origin == types.ListOrigin {
|
||||
newAlert.Scenario = ptr.Of(*decision.Scenario)
|
||||
newAlert.Source.Scope = ptr.Of(types.ListOrigin)
|
||||
} else {
|
||||
var (
|
||||
scenario string
|
||||
scope string
|
||||
)
|
||||
|
||||
switch *decision.Origin {
|
||||
case types.CAPIOrigin:
|
||||
scenario = types.CAPIOrigin
|
||||
scope = types.CAPIOrigin
|
||||
case types.ListOrigin:
|
||||
scenario = *decision.Scenario
|
||||
scope = types.ListOrigin
|
||||
default:
|
||||
// XXX: this or nil?
|
||||
scenario = ""
|
||||
scope = ""
|
||||
log.Warningf("unknown origin %s", *decision.Origin)
|
||||
}
|
||||
newAlert.Message = ptr.Of("")
|
||||
newAlert.Source.Value = ptr.Of("")
|
||||
newAlert.StartAt = ptr.Of(time.Now().UTC().Format(time.RFC3339))
|
||||
newAlert.StopAt = ptr.Of(time.Now().UTC().Format(time.RFC3339))
|
||||
newAlert.Capacity = ptr.Of(int32(0))
|
||||
newAlert.Simulated = ptr.Of(false)
|
||||
newAlert.EventsCount = ptr.Of(int32(0))
|
||||
newAlert.Leakspeed = ptr.Of("")
|
||||
newAlert.ScenarioHash = ptr.Of("")
|
||||
newAlert.ScenarioVersion = ptr.Of("")
|
||||
newAlert.MachineID = database.CapiMachineID
|
||||
return newAlert
|
||||
|
||||
return &models.Alert{
|
||||
Source: &models.Source{
|
||||
Scope: ptr.Of(scope),
|
||||
Value: ptr.Of(""),
|
||||
},
|
||||
Scenario: ptr.Of(scenario),
|
||||
Message: ptr.Of(""),
|
||||
StartAt: ptr.Of(time.Now().UTC().Format(time.RFC3339)),
|
||||
StopAt: ptr.Of(time.Now().UTC().Format(time.RFC3339)),
|
||||
Capacity: ptr.Of(int32(0)),
|
||||
Simulated: ptr.Of(false),
|
||||
EventsCount: ptr.Of(int32(0)),
|
||||
Leakspeed: ptr.Of(""),
|
||||
ScenarioHash: ptr.Of(""),
|
||||
ScenarioVersion: ptr.Of(""),
|
||||
MachineID: database.CapiMachineID,
|
||||
}
|
||||
}
|
||||
|
||||
// This function takes in list of parent alerts and decisions and then pairs them up.
|
||||
|
|
|
@ -16,12 +16,10 @@ import (
|
|||
)
|
||||
|
||||
type Runtime struct {
|
||||
RuntimeFilters []*vm.Program `json:"-" yaml:"-"`
|
||||
DebugFilters []*exprhelpers.ExprDebugger `json:"-" yaml:"-"`
|
||||
RuntimeDurationExpr *vm.Program `json:"-" yaml:"-"`
|
||||
DebugDurationExpr *exprhelpers.ExprDebugger `json:"-" yaml:"-"`
|
||||
Cfg *csconfig.ProfileCfg `json:"-" yaml:"-"`
|
||||
Logger *log.Entry `json:"-" yaml:"-"`
|
||||
RuntimeFilters []*vm.Program `json:"-" yaml:"-"`
|
||||
RuntimeDurationExpr *vm.Program `json:"-" yaml:"-"`
|
||||
Cfg *csconfig.ProfileCfg `json:"-" yaml:"-"`
|
||||
Logger *log.Entry `json:"-" yaml:"-"`
|
||||
}
|
||||
|
||||
var defaultDuration = "4h"
|
||||
|
@ -32,7 +30,6 @@ func NewProfile(profilesCfg []*csconfig.ProfileCfg) ([]*Runtime, error) {
|
|||
|
||||
for _, profile := range profilesCfg {
|
||||
var runtimeFilter, runtimeDurationExpr *vm.Program
|
||||
var debugFilter, debugDurationExpr *exprhelpers.ExprDebugger
|
||||
runtime := &Runtime{}
|
||||
xlog := log.New()
|
||||
if err := types.ConfigureLogger(xlog); err != nil {
|
||||
|
@ -45,7 +42,6 @@ func NewProfile(profilesCfg []*csconfig.ProfileCfg) ([]*Runtime, error) {
|
|||
})
|
||||
|
||||
runtime.RuntimeFilters = make([]*vm.Program, len(profile.Filters))
|
||||
runtime.DebugFilters = make([]*exprhelpers.ExprDebugger, len(profile.Filters))
|
||||
runtime.Cfg = profile
|
||||
if runtime.Cfg.OnSuccess != "" && runtime.Cfg.OnSuccess != "continue" && runtime.Cfg.OnSuccess != "break" {
|
||||
return []*Runtime{}, fmt.Errorf("invalid 'on_success' for '%s': %s", profile.Name, runtime.Cfg.OnSuccess)
|
||||
|
@ -60,12 +56,6 @@ func NewProfile(profilesCfg []*csconfig.ProfileCfg) ([]*Runtime, error) {
|
|||
}
|
||||
runtime.RuntimeFilters[fIdx] = runtimeFilter
|
||||
if profile.Debug != nil && *profile.Debug {
|
||||
if debugFilter, err = exprhelpers.NewDebugger(filter, exprhelpers.GetExprOptions(map[string]interface{}{"Alert": &models.Alert{}})...); err != nil {
|
||||
log.Debugf("Error compiling debug filter of %s : %s", profile.Name, err)
|
||||
// Don't fail if we can't compile the filter - for now
|
||||
// return errors.Wrapf(err, "Error compiling debug filter of %s", profile.Name)
|
||||
}
|
||||
runtime.DebugFilters[fIdx] = debugFilter
|
||||
runtime.Logger.Logger.SetLevel(log.DebugLevel)
|
||||
}
|
||||
}
|
||||
|
@ -74,14 +64,7 @@ func NewProfile(profilesCfg []*csconfig.ProfileCfg) ([]*Runtime, error) {
|
|||
if runtimeDurationExpr, err = expr.Compile(profile.DurationExpr, exprhelpers.GetExprOptions(map[string]interface{}{"Alert": &models.Alert{}})...); err != nil {
|
||||
return []*Runtime{}, errors.Wrapf(err, "error compiling duration_expr of %s", profile.Name)
|
||||
}
|
||||
|
||||
runtime.RuntimeDurationExpr = runtimeDurationExpr
|
||||
if profile.Debug != nil && *profile.Debug {
|
||||
if debugDurationExpr, err = exprhelpers.NewDebugger(profile.DurationExpr, exprhelpers.GetExprOptions(map[string]interface{}{"Alert": &models.Alert{}})...); err != nil {
|
||||
log.Debugf("Error compiling debug duration_expr of %s : %s", profile.Name, err)
|
||||
}
|
||||
runtime.DebugDurationExpr = debugDurationExpr
|
||||
}
|
||||
}
|
||||
|
||||
for _, decision := range profile.Decisions {
|
||||
|
@ -129,7 +112,11 @@ func (Profile *Runtime) GenerateDecisionFromProfile(Alert *models.Alert) ([]*mod
|
|||
/*some fields are populated from the reference object : duration, scope, type*/
|
||||
decision.Duration = new(string)
|
||||
if Profile.Cfg.DurationExpr != "" && Profile.RuntimeDurationExpr != nil {
|
||||
duration, err := expr.Run(Profile.RuntimeDurationExpr, map[string]interface{}{"Alert": Alert})
|
||||
profileDebug := false
|
||||
if Profile.Cfg.Debug != nil && *Profile.Cfg.Debug {
|
||||
profileDebug = true
|
||||
}
|
||||
duration, err := exprhelpers.Run(Profile.RuntimeDurationExpr, map[string]interface{}{"Alert": Alert}, Profile.Logger, profileDebug)
|
||||
if err != nil {
|
||||
Profile.Logger.Warningf("Failed to run duration_expr : %v", err)
|
||||
*decision.Duration = *refDecision.Duration
|
||||
|
@ -173,16 +160,17 @@ func (Profile *Runtime) EvaluateProfile(Alert *models.Alert) ([]*models.Decision
|
|||
|
||||
matched := false
|
||||
for eIdx, expression := range Profile.RuntimeFilters {
|
||||
output, err := expr.Run(expression, map[string]interface{}{"Alert": Alert})
|
||||
debugProfile := false
|
||||
if Profile.Cfg.Debug != nil && *Profile.Cfg.Debug {
|
||||
debugProfile = true
|
||||
}
|
||||
output, err := exprhelpers.Run(expression, map[string]interface{}{"Alert": Alert}, Profile.Logger, debugProfile)
|
||||
if err != nil {
|
||||
Profile.Logger.Warningf("failed to run profile expr for %s : %v", Profile.Cfg.Name, err)
|
||||
return nil, matched, errors.Wrapf(err, "while running expression %s", Profile.Cfg.Filters[eIdx])
|
||||
}
|
||||
switch out := output.(type) {
|
||||
case bool:
|
||||
if Profile.Cfg.Debug != nil && *Profile.Cfg.Debug {
|
||||
Profile.DebugFilters[eIdx].Run(Profile.Logger, out, map[string]interface{}{"Alert": Alert})
|
||||
}
|
||||
if out {
|
||||
matched = true
|
||||
/*the expression matched, create the associated decision*/
|
||||
|
|
|
@ -168,7 +168,6 @@ func (i *Item) removeInstallLink() error {
|
|||
|
||||
// disable removes the install link, and optionally the downloaded content.
|
||||
func (i *Item) disable(purge bool, force bool) error {
|
||||
// XXX: should return the number of disabled/purged items to inform the upper layer whether to reload or not
|
||||
err := i.removeInstallLink()
|
||||
if os.IsNotExist(err) {
|
||||
if !purge && !force {
|
||||
|
|
|
@ -2,8 +2,6 @@ package cwhub
|
|||
|
||||
// Install, upgrade and remove items from the hub to the local configuration
|
||||
|
||||
// XXX: this file could use a better name
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
|
@ -29,20 +27,16 @@ func (i *Item) Install(force bool, downloadOnly bool) error {
|
|||
}
|
||||
}
|
||||
|
||||
// XXX: confusing semantic between force and updateOnly?
|
||||
filePath, err := i.downloadLatest(force, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("while downloading %s: %w", i.Name, err)
|
||||
}
|
||||
|
||||
if downloadOnly {
|
||||
// XXX: should get the path from downloadLatest
|
||||
log.Infof("Downloaded %s to %s", i.Name, filePath)
|
||||
return nil
|
||||
}
|
||||
|
||||
// XXX: should we stop here if the item is already installed?
|
||||
|
||||
if err := i.enable(); err != nil {
|
||||
return fmt.Errorf("while enabling %s: %w", i.Name, err)
|
||||
}
|
||||
|
@ -52,8 +46,8 @@ func (i *Item) Install(force bool, downloadOnly bool) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// allDependencies returns a list of all (direct or indirect) dependencies of the item.
|
||||
func (i *Item) allDependencies() ([]*Item, error) {
|
||||
// descendants returns a list of all (direct or indirect) dependencies of the item.
|
||||
func (i *Item) descendants() ([]*Item, error) {
|
||||
var collectSubItems func(item *Item, visited map[*Item]bool, result *[]*Item) error
|
||||
|
||||
collectSubItems = func(item *Item, visited map[*Item]bool, result *[]*Item) error {
|
||||
|
@ -111,11 +105,13 @@ func (i *Item) Remove(purge bool, force bool) (bool, error) {
|
|||
|
||||
removed := false
|
||||
|
||||
allDeps, err := i.allDependencies()
|
||||
descendants, err := i.descendants()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
ancestors := i.Ancestors()
|
||||
|
||||
for _, sub := range i.SubItems() {
|
||||
if !sub.State.Installed {
|
||||
continue
|
||||
|
@ -123,16 +119,25 @@ func (i *Item) Remove(purge bool, force bool) (bool, error) {
|
|||
|
||||
// if the sub depends on a collection that is not a direct or indirect dependency
|
||||
// of the current item, it is not removed
|
||||
for _, subParent := range sub.AncestorCollections() {
|
||||
for _, subParent := range sub.Ancestors() {
|
||||
if !purge && !subParent.State.Installed {
|
||||
continue
|
||||
}
|
||||
|
||||
// the ancestor that would block the removal of the sub item is also an ancestor
|
||||
// of the item we are removing, so we don't want false warnings
|
||||
// (e.g. crowdsecurity/sshd-logs was not removed because it also belongs to crowdsecurity/linux,
|
||||
// while we are removing crowdsecurity/sshd)
|
||||
if slices.Contains(ancestors, subParent) {
|
||||
continue
|
||||
}
|
||||
|
||||
// the sub-item belongs to the item we are removing, but we already knew that
|
||||
if subParent == i {
|
||||
continue
|
||||
}
|
||||
|
||||
if !slices.Contains(allDeps, subParent) {
|
||||
if !slices.Contains(descendants, subParent) {
|
||||
log.Infof("%s was not removed because it also belongs to %s", sub.Name, subParent.Name)
|
||||
continue
|
||||
}
|
||||
|
@ -150,7 +155,6 @@ func (i *Item) Remove(purge bool, force bool) (bool, error) {
|
|||
return false, fmt.Errorf("while removing %s: %w", i.Name, err)
|
||||
}
|
||||
|
||||
// XXX: should take the value from disable()
|
||||
removed = true
|
||||
|
||||
return removed, nil
|
||||
|
@ -187,7 +191,7 @@ func (i *Item) Upgrade(force bool) (bool, error) {
|
|||
|
||||
if !i.State.UpToDate {
|
||||
if i.State.Tainted {
|
||||
log.Infof("%v %s is tainted, --force to overwrite", emoji.Warning, i.Name)
|
||||
log.Warningf("%v %s is tainted, --force to overwrite", emoji.Warning, i.Name)
|
||||
} else if i.IsLocal() {
|
||||
log.Infof("%v %s is local", emoji.Prohibited, i.Name)
|
||||
}
|
||||
|
@ -205,7 +209,6 @@ func (i *Item) Upgrade(force bool) (bool, error) {
|
|||
|
||||
// downloadLatest downloads the latest version of the item to the hub directory.
|
||||
func (i *Item) downloadLatest(overwrite bool, updateOnly bool) (string, error) {
|
||||
// XXX: should return the path of the downloaded file (taken from download())
|
||||
log.Debugf("Downloading %s %s", i.Type, i.Name)
|
||||
|
||||
for _, sub := range i.SubItems() {
|
||||
|
|
|
@ -184,7 +184,7 @@ func assertCollectionDepsInstalled(t *testing.T, hub *Hub, collection string) {
|
|||
t.Helper()
|
||||
|
||||
c := hub.Items[COLLECTIONS][collection]
|
||||
require.NoError(t, hub.checkSubItems(c))
|
||||
require.NoError(t, c.checkSubItemVersions())
|
||||
}
|
||||
|
||||
func pushUpdateToCollectionInHub() {
|
||||
|
|
|
@ -39,16 +39,16 @@ type HubItems map[string]map[string]*Item
|
|||
// by comparing the hash of each version to the local file.
|
||||
// If the item does not match any known version, it is considered tainted (modified).
|
||||
type ItemVersion struct {
|
||||
Digest string `json:"digest,omitempty"` // meow
|
||||
Deprecated bool `json:"deprecated,omitempty"`
|
||||
Digest string `json:"digest,omitempty" yaml:"digest,omitempty"`
|
||||
Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
|
||||
}
|
||||
|
||||
// ItemState is used to keep the local state (i.e. at runtime) of an item.
|
||||
// This data is not stored in the index, but is displayed with "cscli ... inspect".
|
||||
type ItemState struct {
|
||||
LocalPath string `json:"local_path,omitempty" yaml:"local_path,omitempty"`
|
||||
LocalVersion string `json:"local_version,omitempty"`
|
||||
LocalHash string `json:"local_hash,omitempty"`
|
||||
LocalVersion string `json:"local_version,omitempty" yaml:"local_version,omitempty"`
|
||||
LocalHash string `json:"local_hash,omitempty" yaml:"local_hash,omitempty"`
|
||||
Installed bool `json:"installed"`
|
||||
Downloaded bool `json:"downloaded"`
|
||||
UpToDate bool `json:"up_to_date"`
|
||||
|
@ -62,23 +62,23 @@ type Item struct {
|
|||
|
||||
State ItemState `json:"-" yaml:"-"` // local state, not stored in the index
|
||||
|
||||
Type string `json:"type,omitempty" yaml:"type,omitempty"` // one of the ItemTypes
|
||||
Stage string `json:"stage,omitempty" yaml:"stage,omitempty"` // Stage for parser|postoverflow: s00-raw/s01-...
|
||||
Name string `json:"name,omitempty"` // usually "author/name"
|
||||
FileName string `json:"file_name,omitempty"` // eg. apache2-logs.yaml
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Author string `json:"author,omitempty"`
|
||||
References []string `json:"references,omitempty" yaml:"references,omitempty"`
|
||||
Type string `json:"type,omitempty" yaml:"type,omitempty"` // one of the ItemTypes
|
||||
Stage string `json:"stage,omitempty" yaml:"stage,omitempty"` // Stage for parser|postoverflow: s00-raw/s01-...
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"` // usually "author/name"
|
||||
FileName string `json:"file_name,omitempty" yaml:"file_name,omitempty"` // eg. apache2-logs.yaml
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Author string `json:"author,omitempty" yaml:"author,omitempty"`
|
||||
References []string `json:"references,omitempty" yaml:"references,omitempty"`
|
||||
|
||||
RemotePath string `json:"path,omitempty" yaml:"remote_path,omitempty"` // path relative to the base URL eg. /parsers/stage/author/file.yaml
|
||||
Version string `json:"version,omitempty"` // the last available version
|
||||
Versions map[string]ItemVersion `json:"versions,omitempty" yaml:"-"` // all the known versions
|
||||
RemotePath string `json:"path,omitempty" yaml:"remote_path,omitempty"` // path relative to the base URL eg. /parsers/stage/author/file.yaml
|
||||
Version string `json:"version,omitempty" yaml:"version,omitempty"` // the last available version
|
||||
Versions map[string]ItemVersion `json:"versions,omitempty" yaml:"-"` // all the known versions
|
||||
|
||||
// if it's a collection, it can have sub items
|
||||
Parsers []string `json:"parsers,omitempty" yaml:"parsers,omitempty"`
|
||||
Parsers []string `json:"parsers,omitempty" yaml:"parsers,omitempty"`
|
||||
PostOverflows []string `json:"postoverflows,omitempty" yaml:"postoverflows,omitempty"`
|
||||
Scenarios []string `json:"scenarios,omitempty" yaml:"scenarios,omitempty"`
|
||||
Collections []string `json:"collections,omitempty" yaml:"collections,omitempty"`
|
||||
Scenarios []string `json:"scenarios,omitempty" yaml:"scenarios,omitempty"`
|
||||
Collections []string `json:"collections,omitempty" yaml:"collections,omitempty"`
|
||||
WaapConfigs []string `json:"waap-configs,omitempty" yaml:"waap-configs,omitempty"`
|
||||
WaapRules []string `json:"waap-rules,omitempty" yaml:"waap-rules,omitempty"`
|
||||
}
|
||||
|
@ -243,8 +243,8 @@ func (i *Item) logMissingSubItems() {
|
|||
}
|
||||
}
|
||||
|
||||
// AncestorCollections returns a slice of items (collections) that have this item as a direct or indirect dependency.
|
||||
func (i *Item) AncestorCollections() []*Item {
|
||||
// Ancestors returns a slice of items (typically collections) that have this item as a direct or indirect dependency.
|
||||
func (i *Item) Ancestors() []*Item {
|
||||
ret := make([]*Item, 0)
|
||||
|
||||
for _, parentName := range i.State.BelongsToCollections {
|
||||
|
@ -259,9 +259,9 @@ func (i *Item) AncestorCollections() []*Item {
|
|||
return ret
|
||||
}
|
||||
|
||||
// Status returns the status of the item as a string and an emoji
|
||||
// InstallStatus returns the status of the item as a string and an emoji
|
||||
// (eg. "enabled,update-available" and emoji.Warning).
|
||||
func (i *Item) Status() (string, emoji.Emoji) {
|
||||
func (i *Item) InstallStatus() (string, emoji.Emoji) {
|
||||
status := "disabled"
|
||||
ok := false
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ func TestItemStatus(t *testing.T) {
|
|||
item.State.Tainted = false
|
||||
item.State.Downloaded = true
|
||||
|
||||
txt, _ := item.Status()
|
||||
txt, _ := item.InstallStatus()
|
||||
require.Equal(t, "enabled,update-available", txt)
|
||||
|
||||
item.State.Installed = true
|
||||
|
@ -31,7 +31,7 @@ func TestItemStatus(t *testing.T) {
|
|||
item.State.Tainted = false
|
||||
item.State.Downloaded = false
|
||||
|
||||
txt, _ = item.Status()
|
||||
txt, _ = item.InstallStatus()
|
||||
require.Equal(t, "enabled,local", txt)
|
||||
}
|
||||
|
||||
|
|
|
@ -15,10 +15,6 @@ import (
|
|||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type localItem struct {
|
||||
Name string `yaml:"name"`
|
||||
}
|
||||
|
||||
func isYAMLFileName(path string) bool {
|
||||
return strings.HasSuffix(path, ".yaml") || strings.HasSuffix(path, ".yml")
|
||||
}
|
||||
|
@ -307,53 +303,51 @@ func (h *Hub) itemVisit(path string, f os.DirEntry, err error) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// checkSubItems checks for the presence, taint and version state of sub-items.
|
||||
func (h *Hub) checkSubItems(v *Item) error {
|
||||
if !v.HasSubItems() {
|
||||
// checkSubItemVersions checks for the presence, taint and version state of sub-items.
|
||||
func (i *Item) checkSubItemVersions() error {
|
||||
if !i.HasSubItems() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if v.versionStatus() != versionUpToDate {
|
||||
log.Debugf("%s dependencies not checked: not up-to-date", v.Name)
|
||||
if i.versionStatus() != versionUpToDate {
|
||||
log.Debugf("%s dependencies not checked: not up-to-date", i.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ensure all the sub-items are installed, or tag the parent as tainted
|
||||
log.Tracef("checking submembers of %s installed:%t", v.Name, v.State.Installed)
|
||||
log.Tracef("checking submembers of %s installed:%t", i.Name, i.State.Installed)
|
||||
|
||||
for _, sub := range v.SubItems() {
|
||||
for _, sub := range i.SubItems() {
|
||||
log.Tracef("check %s installed:%t", sub.Name, sub.State.Installed)
|
||||
|
||||
if !v.State.Installed {
|
||||
if !i.State.Installed {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := h.checkSubItems(sub); err != nil {
|
||||
if err := sub.checkSubItemVersions(); err != nil {
|
||||
if sub.State.Tainted {
|
||||
v.State.Tainted = true
|
||||
i.State.Tainted = true
|
||||
}
|
||||
|
||||
return fmt.Errorf("sub collection %s is broken: %w", sub.Name, err)
|
||||
return fmt.Errorf("dependency of %s: sub collection %s is broken: %w", i.Name, sub.Name, err)
|
||||
}
|
||||
|
||||
if sub.State.Tainted {
|
||||
v.State.Tainted = true
|
||||
// XXX: improve msg
|
||||
return fmt.Errorf("tainted %s %s, tainted", sub.Type, sub.Name)
|
||||
i.State.Tainted = true
|
||||
return fmt.Errorf("%s is tainted because %s:%s is tainted", i.Name, sub.Type, sub.Name)
|
||||
}
|
||||
|
||||
if !sub.State.Installed && v.State.Installed {
|
||||
v.State.Tainted = true
|
||||
// XXX: improve msg
|
||||
return fmt.Errorf("missing %s %s, tainted", sub.Type, sub.Name)
|
||||
if !sub.State.Installed && i.State.Installed {
|
||||
i.State.Tainted = true
|
||||
return fmt.Errorf("%s is tainted because %s:%s is missing", i.Name, sub.Type, sub.Name)
|
||||
}
|
||||
|
||||
if !sub.State.UpToDate {
|
||||
v.State.UpToDate = false
|
||||
return fmt.Errorf("outdated %s %s", sub.Type, sub.Name)
|
||||
i.State.UpToDate = false
|
||||
return fmt.Errorf("dependency of %s: outdated %s:%s", i.Name, sub.Type, sub.Name)
|
||||
}
|
||||
|
||||
log.Tracef("checking for %s - tainted:%t uptodate:%t", sub.Name, v.State.Tainted, v.State.UpToDate)
|
||||
log.Tracef("checking for %s - tainted:%t uptodate:%t", sub.Name, i.State.Tainted, i.State.UpToDate)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -409,7 +403,7 @@ func (h *Hub) localSync() error {
|
|||
|
||||
for _, item := range h.Items[COLLECTIONS] {
|
||||
// check for cyclic dependencies
|
||||
subs, err := item.allDependencies()
|
||||
subs, err := item.descendants()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -426,8 +420,8 @@ func (h *Hub) localSync() error {
|
|||
vs := item.versionStatus()
|
||||
switch vs {
|
||||
case versionUpToDate: // latest
|
||||
if err := h.checkSubItems(item); err != nil {
|
||||
warnings = append(warnings, fmt.Sprintf("dependency of %s: %s", item.Name, err))
|
||||
if err := item.checkSubItemVersions(); err != nil {
|
||||
warnings = append(warnings, err.Error())
|
||||
}
|
||||
case versionUpdateAvailable: // not up-to-date
|
||||
warnings = append(warnings, fmt.Sprintf("update for collection %s available (currently:%s, latest:%s)", item.Name, item.State.LocalVersion, item.Version))
|
||||
|
|
|
@ -9,6 +9,8 @@ import (
|
|||
"entgo.io/ent/dialect/sql"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/crowdsecurity/go-cs-lib/slicetools"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/database/ent"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/database/ent/decision"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/database/ent/predicate"
|
||||
|
@ -23,7 +25,6 @@ type DecisionsByScenario struct {
|
|||
}
|
||||
|
||||
func BuildDecisionRequestWithFilter(query *ent.DecisionQuery, filter map[string][]string) (*ent.DecisionQuery, error) {
|
||||
|
||||
var err error
|
||||
var start_ip, start_sfx, end_ip, end_sfx int64
|
||||
var ip_sz int
|
||||
|
@ -545,55 +546,39 @@ func (c *Client) SoftDeleteDecisionsWithFilter(filter map[string][]string) (stri
|
|||
|
||||
// BulkDeleteDecisions set the expiration of a bulk of decisions to now() or hard deletes them.
|
||||
// We are doing it this way so we can return impacted decisions for sync with CAPI/PAPI
|
||||
func (c *Client) BulkDeleteDecisions(DecisionsToDelete []*ent.Decision, softDelete bool) (int, error) {
|
||||
bulkSize := 256 //scientifically proven to be the best value for bulk delete
|
||||
idsToDelete := make([]int, 0, bulkSize)
|
||||
totalUpdates := 0
|
||||
for i := 0; i < len(DecisionsToDelete); i++ {
|
||||
idsToDelete = append(idsToDelete, DecisionsToDelete[i].ID)
|
||||
if len(idsToDelete) == bulkSize {
|
||||
func (c *Client) BulkDeleteDecisions(decisionsToDelete []*ent.Decision, softDelete bool) (int, error) {
|
||||
const bulkSize = 256 //scientifically proven to be the best value for bulk delete
|
||||
|
||||
if softDelete {
|
||||
nbUpdates, err := c.Ent.Decision.Update().Where(
|
||||
decision.IDIn(idsToDelete...),
|
||||
).SetUntil(time.Now().UTC()).Save(c.CTX)
|
||||
if err != nil {
|
||||
return totalUpdates, errors.Wrap(err, "soft delete decisions with provided filter")
|
||||
}
|
||||
totalUpdates += nbUpdates
|
||||
} else {
|
||||
nbUpdates, err := c.Ent.Decision.Delete().Where(
|
||||
decision.IDIn(idsToDelete...),
|
||||
).Exec(c.CTX)
|
||||
if err != nil {
|
||||
return totalUpdates, errors.Wrap(err, "hard delete decisions with provided filter")
|
||||
}
|
||||
totalUpdates += nbUpdates
|
||||
}
|
||||
idsToDelete = make([]int, 0, bulkSize)
|
||||
}
|
||||
var (
|
||||
nbUpdates int
|
||||
err error
|
||||
totalUpdates = 0
|
||||
)
|
||||
|
||||
idsToDelete := make([]int, len(decisionsToDelete))
|
||||
for i, decision := range decisionsToDelete {
|
||||
idsToDelete[i] = decision.ID
|
||||
}
|
||||
|
||||
if len(idsToDelete) > 0 {
|
||||
for _, chunk := range slicetools.Chunks(idsToDelete, bulkSize) {
|
||||
if softDelete {
|
||||
nbUpdates, err := c.Ent.Decision.Update().Where(
|
||||
decision.IDIn(idsToDelete...),
|
||||
nbUpdates, err = c.Ent.Decision.Update().Where(
|
||||
decision.IDIn(chunk...),
|
||||
).SetUntil(time.Now().UTC()).Save(c.CTX)
|
||||
if err != nil {
|
||||
return totalUpdates, errors.Wrap(err, "soft delete decisions with provided filter")
|
||||
return totalUpdates, fmt.Errorf("soft delete decisions with provided filter: %w", err)
|
||||
}
|
||||
totalUpdates += nbUpdates
|
||||
} else {
|
||||
nbUpdates, err := c.Ent.Decision.Delete().Where(
|
||||
decision.IDIn(idsToDelete...),
|
||||
nbUpdates, err = c.Ent.Decision.Delete().Where(
|
||||
decision.IDIn(chunk...),
|
||||
).Exec(c.CTX)
|
||||
if err != nil {
|
||||
return totalUpdates, errors.Wrap(err, "hard delete decisions with provided filter")
|
||||
return totalUpdates, fmt.Errorf("hard delete decisions with provided filter: %w", err)
|
||||
}
|
||||
totalUpdates += nbUpdates
|
||||
}
|
||||
|
||||
totalUpdates += nbUpdates
|
||||
}
|
||||
|
||||
return totalUpdates, nil
|
||||
}
|
||||
|
||||
|
@ -601,6 +586,7 @@ func (c *Client) BulkDeleteDecisions(DecisionsToDelete []*ent.Decision, softDele
|
|||
func (c *Client) SoftDeleteDecisionByID(decisionID int) (int, []*ent.Decision, error) {
|
||||
toUpdate, err := c.Ent.Decision.Query().Where(decision.IDEQ(decisionID)).All(c.CTX)
|
||||
|
||||
// XXX: do we want 500 or 404 here?
|
||||
if err != nil || len(toUpdate) == 0 {
|
||||
c.Log.Warningf("SoftDeleteDecisionByID : %v (nb soft deleted: %d)", err, len(toUpdate))
|
||||
return 0, nil, errors.Wrapf(DeleteFail, "decision with id '%d' doesn't exist", decisionID)
|
||||
|
@ -609,6 +595,7 @@ func (c *Client) SoftDeleteDecisionByID(decisionID int) (int, []*ent.Decision, e
|
|||
if len(toUpdate) == 0 {
|
||||
return 0, nil, ItemNotFound
|
||||
}
|
||||
|
||||
count, err := c.BulkDeleteDecisions(toUpdate, true)
|
||||
return count, toUpdate, err
|
||||
}
|
||||
|
@ -639,10 +626,7 @@ func (c *Client) CountDecisionsByValue(decisionValue string) (int, error) {
|
|||
}
|
||||
|
||||
func (c *Client) CountDecisionsSinceByValue(decisionValue string, since time.Time) (int, error) {
|
||||
var err error
|
||||
var start_ip, start_sfx, end_ip, end_sfx int64
|
||||
var ip_sz, count int
|
||||
ip_sz, start_ip, start_sfx, end_ip, end_sfx, err = types.Addr2Ints(decisionValue)
|
||||
ip_sz, start_ip, start_sfx, end_ip, end_sfx, err := types.Addr2Ints(decisionValue)
|
||||
|
||||
if err != nil {
|
||||
return 0, errors.Wrapf(InvalidIPOrRange, "unable to convert '%s' to int: %s", decisionValue, err)
|
||||
|
@ -652,11 +636,13 @@ func (c *Client) CountDecisionsSinceByValue(decisionValue string, since time.Tim
|
|||
decisions := c.Ent.Decision.Query().Where(
|
||||
decision.CreatedAtGT(since),
|
||||
)
|
||||
|
||||
decisions, err = applyStartIpEndIpFilter(decisions, contains, ip_sz, start_ip, start_sfx, end_ip, end_sfx)
|
||||
if err != nil {
|
||||
return 0, errors.Wrapf(err, "fail to apply StartIpEndIpFilter")
|
||||
}
|
||||
count, err = decisions.Count(c.CTX)
|
||||
|
||||
count, err := decisions.Count(c.CTX)
|
||||
if err != nil {
|
||||
return 0, errors.Wrapf(err, "fail to count decisions")
|
||||
}
|
||||
|
@ -681,7 +667,10 @@ func applyStartIpEndIpFilter(decisions *ent.DecisionQuery, contains bool, ip_sz
|
|||
decision.IPSizeEQ(int64(ip_sz)),
|
||||
))
|
||||
}
|
||||
} else if ip_sz == 16 {
|
||||
return decisions, nil
|
||||
}
|
||||
|
||||
if ip_sz == 16 {
|
||||
/*decision contains {start_ip,end_ip}*/
|
||||
if contains {
|
||||
decisions = decisions.Where(decision.And(
|
||||
|
@ -733,9 +722,13 @@ func applyStartIpEndIpFilter(decisions *ent.DecisionQuery, contains bool, ip_sz
|
|||
),
|
||||
))
|
||||
}
|
||||
} else if ip_sz != 0 {
|
||||
return decisions, nil
|
||||
}
|
||||
|
||||
if ip_sz != 0 {
|
||||
return nil, errors.Wrapf(InvalidFilter, "unknown ip size %d", ip_sz)
|
||||
}
|
||||
|
||||
return decisions, nil
|
||||
}
|
||||
|
||||
|
|
462
pkg/exprhelpers/debugger.go
Normal file
462
pkg/exprhelpers/debugger.go
Normal file
|
@ -0,0 +1,462 @@
|
|||
package exprhelpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/antonmedv/expr"
|
||||
"github.com/antonmedv/expr/vm"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type ExprRuntimeDebug struct {
|
||||
Logger *log.Entry
|
||||
Lines []string
|
||||
Outputs []OpOutput
|
||||
}
|
||||
|
||||
var IndentStep = 4
|
||||
|
||||
// we use this struct to store the output of the expr runtime
|
||||
type OpOutput struct {
|
||||
Code string //relevant code part
|
||||
|
||||
CodeDepth int //level of nesting
|
||||
BlockStart bool
|
||||
BlockEnd bool
|
||||
|
||||
Func bool //true if it's a function call
|
||||
FuncName string
|
||||
Args []string
|
||||
FuncResults []string
|
||||
//
|
||||
Comparison bool //true if it's a comparison
|
||||
Negated bool
|
||||
Left string
|
||||
Right string
|
||||
//
|
||||
JumpIf bool //true if it's conditional jump
|
||||
IfTrue bool
|
||||
IfFalse bool
|
||||
//
|
||||
Condition bool //true if it's a condition
|
||||
ConditionIn bool
|
||||
ConditionContains bool
|
||||
//used for comparisons, conditional jumps and conditions
|
||||
StrConditionResult string
|
||||
ConditionResult *bool //should always be present for conditions
|
||||
|
||||
//
|
||||
Finalized bool //used when a node is finalized, we already fetched result from next OP
|
||||
}
|
||||
|
||||
func (o *OpOutput) String() string {
|
||||
|
||||
ret := fmt.Sprintf("%*c", o.CodeDepth, ' ')
|
||||
if len(o.Code) != 0 {
|
||||
ret += fmt.Sprintf("[%s]", o.Code)
|
||||
}
|
||||
ret += " "
|
||||
|
||||
switch {
|
||||
case o.BlockStart:
|
||||
ret = fmt.Sprintf("%*cBLOCK_START [%s]", o.CodeDepth-IndentStep, ' ', o.Code)
|
||||
return ret
|
||||
case o.BlockEnd:
|
||||
indent := o.CodeDepth - (IndentStep * 2)
|
||||
if indent < 0 {
|
||||
indent = 0
|
||||
}
|
||||
ret = fmt.Sprintf("%*cBLOCK_END [%s]", indent, ' ', o.Code)
|
||||
if len(o.StrConditionResult) > 0 {
|
||||
ret += fmt.Sprintf(" -> %s", o.StrConditionResult)
|
||||
}
|
||||
return ret
|
||||
//A block end can carry a value, for example if it's a count, any, all etc. XXX
|
||||
case o.Func:
|
||||
return ret + fmt.Sprintf("%s(%s) = %s", o.FuncName, strings.Join(o.Args, ", "), strings.Join(o.FuncResults, ", "))
|
||||
case o.Comparison:
|
||||
if o.Negated {
|
||||
ret += "NOT "
|
||||
}
|
||||
ret += fmt.Sprintf("%s == %s -> %s", o.Left, o.Right, o.StrConditionResult)
|
||||
return ret
|
||||
case o.ConditionIn:
|
||||
return ret + fmt.Sprintf("%s in %s -> %s", o.Args[0], o.Args[1], o.StrConditionResult)
|
||||
case o.ConditionContains:
|
||||
return ret + fmt.Sprintf("%s contains %s -> %s", o.Args[0], o.Args[1], o.StrConditionResult)
|
||||
case o.JumpIf && o.IfTrue:
|
||||
if o.ConditionResult != nil {
|
||||
if *o.ConditionResult {
|
||||
return ret + "OR -> false"
|
||||
}
|
||||
return ret + "OR -> true"
|
||||
}
|
||||
return ret + "OR(?)"
|
||||
case o.JumpIf && o.IfFalse:
|
||||
if o.ConditionResult != nil {
|
||||
if *o.ConditionResult {
|
||||
return ret + "AND -> true"
|
||||
}
|
||||
return ret + "AND -> false"
|
||||
}
|
||||
return ret + "AND(?)"
|
||||
}
|
||||
return ret + ""
|
||||
}
|
||||
|
||||
func (erp ExprRuntimeDebug) extractCode(ip int, program *vm.Program, parts []string) string {
|
||||
|
||||
//log.Tracef("# extracting code for ip %d [%s]", ip, parts[1])
|
||||
if program.Locations[ip].Line == 0 { //it seems line is zero when it's not actual code (ie. op push at the beginning)
|
||||
log.Tracef("zero location ?")
|
||||
return ""
|
||||
}
|
||||
startLine := program.Locations[ip].Line
|
||||
startColumn := program.Locations[ip].Column
|
||||
lines := strings.Split(program.Source.Content(), "\n")
|
||||
|
||||
endCol := 0
|
||||
endLine := 0
|
||||
|
||||
for i := ip + 1; i < len(program.Locations); i++ {
|
||||
if program.Locations[i].Line > startLine || (program.Locations[i].Line == startLine && program.Locations[i].Column > startColumn) {
|
||||
//we didn't had values yet and it's superior to current one, take it
|
||||
if endLine == 0 && endCol == 0 {
|
||||
endLine = program.Locations[i].Line
|
||||
endCol = program.Locations[i].Column
|
||||
}
|
||||
//however, we are looking for the closest upper one
|
||||
if program.Locations[i].Line < endLine || (program.Locations[i].Line == endLine && program.Locations[i].Column < endCol) {
|
||||
endLine = program.Locations[i].Line
|
||||
endCol = program.Locations[i].Column
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//maybe it was the last instruction ?
|
||||
if endCol == 0 && endLine == 0 {
|
||||
endLine = len(lines)
|
||||
endCol = len(lines[endLine-1])
|
||||
}
|
||||
code_snippet := ""
|
||||
startLine -= 1 //line count starts at 1
|
||||
endLine -= 1
|
||||
|
||||
for i := startLine; i <= endLine; i++ {
|
||||
if i == startLine {
|
||||
if startLine != endLine {
|
||||
code_snippet += lines[i][startColumn:]
|
||||
continue
|
||||
}
|
||||
code_snippet += lines[i][startColumn:endCol]
|
||||
break
|
||||
}
|
||||
if i == endLine {
|
||||
code_snippet += lines[i][:endCol]
|
||||
break
|
||||
}
|
||||
code_snippet += lines[i]
|
||||
}
|
||||
|
||||
log.Tracef("#code extract for ip %d [%s] -> '%s'", ip, parts[1], code_snippet)
|
||||
return cleanTextForDebug(code_snippet)
|
||||
}
|
||||
|
||||
func autoQuote(v any) string {
|
||||
switch x := v.(type) {
|
||||
case string:
|
||||
//let's avoid printing long strings. it can happen ie. when we are debugging expr with `File()` or similar helpers
|
||||
if len(x) > 40 {
|
||||
return fmt.Sprintf("%q", x[:40]+"...")
|
||||
} else {
|
||||
return fmt.Sprintf("%q", x)
|
||||
}
|
||||
default:
|
||||
return fmt.Sprintf("%v", x)
|
||||
}
|
||||
}
|
||||
|
||||
func (erp ExprRuntimeDebug) ipDebug(ip int, vm *vm.VM, program *vm.Program, parts []string, outputs []OpOutput) ([]OpOutput, error) {
|
||||
|
||||
IdxOut := len(outputs)
|
||||
prevIdxOut := 0
|
||||
currentDepth := 0
|
||||
|
||||
//when there is a function call or comparison, we need to wait for the next instruction to get the result and "finalize" the previous one
|
||||
if IdxOut > 0 {
|
||||
prevIdxOut = IdxOut - 1
|
||||
currentDepth = outputs[prevIdxOut].CodeDepth
|
||||
if outputs[prevIdxOut].Func && !outputs[prevIdxOut].Finalized {
|
||||
stack := vm.Stack()
|
||||
num_items := 1
|
||||
for i := len(stack) - 1; i >= 0 && num_items > 0; i-- {
|
||||
outputs[prevIdxOut].FuncResults = append(outputs[prevIdxOut].FuncResults, autoQuote(stack[i]))
|
||||
num_items--
|
||||
}
|
||||
outputs[prevIdxOut].Finalized = true
|
||||
} else if (outputs[prevIdxOut].Comparison || outputs[prevIdxOut].Condition) && !outputs[prevIdxOut].Finalized {
|
||||
stack := vm.Stack()
|
||||
outputs[prevIdxOut].StrConditionResult = fmt.Sprintf("%+v", stack)
|
||||
if val, ok := stack[0].(bool); ok {
|
||||
outputs[prevIdxOut].ConditionResult = new(bool)
|
||||
*outputs[prevIdxOut].ConditionResult = val
|
||||
}
|
||||
outputs[prevIdxOut].Finalized = true
|
||||
}
|
||||
}
|
||||
|
||||
erp.Logger.Tracef("[STEP %d:%s] (stack:%+v) (parts:%+v) {depth:%d}", ip, parts[1], vm.Stack(), parts, currentDepth)
|
||||
out := OpOutput{}
|
||||
out.CodeDepth = currentDepth
|
||||
out.Code = erp.extractCode(ip, program, parts)
|
||||
|
||||
switch parts[1] {
|
||||
case "OpBegin":
|
||||
out.CodeDepth += IndentStep
|
||||
out.BlockStart = true
|
||||
outputs = append(outputs, out)
|
||||
case "OpEnd":
|
||||
out.CodeDepth -= IndentStep
|
||||
out.BlockEnd = true
|
||||
//OpEnd can carry value, if it's any/all/count etc.
|
||||
if len(vm.Stack()) > 0 {
|
||||
out.StrConditionResult = fmt.Sprintf("%v", vm.Stack())
|
||||
}
|
||||
outputs = append(outputs, out)
|
||||
case "OpNot":
|
||||
//negate the previous condition
|
||||
outputs[prevIdxOut].Negated = true
|
||||
case "OpTrue": //generated when possible ? (1 == 1)
|
||||
out.Condition = true
|
||||
out.ConditionResult = new(bool)
|
||||
*out.ConditionResult = true
|
||||
out.StrConditionResult = "true"
|
||||
outputs = append(outputs, out)
|
||||
case "OpFalse": //generated when possible ? (1 != 1)
|
||||
out.Condition = true
|
||||
out.ConditionResult = new(bool)
|
||||
*out.ConditionResult = false
|
||||
out.StrConditionResult = "false"
|
||||
outputs = append(outputs, out)
|
||||
case "OpJumpIfTrue": //OR
|
||||
stack := vm.Stack()
|
||||
out.JumpIf = true
|
||||
out.IfTrue = true
|
||||
out.StrConditionResult = fmt.Sprintf("%v", stack[0])
|
||||
|
||||
if val, ok := stack[0].(bool); ok {
|
||||
out.ConditionResult = new(bool)
|
||||
*out.ConditionResult = val
|
||||
}
|
||||
outputs = append(outputs, out)
|
||||
case "OpJumpIfFalse": //AND
|
||||
stack := vm.Stack()
|
||||
out.JumpIf = true
|
||||
out.IfFalse = true
|
||||
out.StrConditionResult = fmt.Sprintf("%v", stack[0])
|
||||
if val, ok := stack[0].(bool); ok {
|
||||
out.ConditionResult = new(bool)
|
||||
*out.ConditionResult = val
|
||||
}
|
||||
outputs = append(outputs, out)
|
||||
case "OpCall1": //Op for function calls
|
||||
out.Func = true
|
||||
out.FuncName = parts[3]
|
||||
stack := vm.Stack()
|
||||
num_items := 1
|
||||
for i := len(stack) - 1; i >= 0 && num_items > 0; i-- {
|
||||
out.Args = append(out.Args, autoQuote(stack[i]))
|
||||
num_items--
|
||||
}
|
||||
outputs = append(outputs, out)
|
||||
case "OpCall2": //Op for function calls
|
||||
out.Func = true
|
||||
out.FuncName = parts[3]
|
||||
stack := vm.Stack()
|
||||
num_items := 2
|
||||
for i := len(stack) - 1; i >= 0 && num_items > 0; i-- {
|
||||
out.Args = append(out.Args, autoQuote(stack[i]))
|
||||
num_items--
|
||||
}
|
||||
outputs = append(outputs, out)
|
||||
case "OpCall3": //Op for function calls
|
||||
out.Func = true
|
||||
out.FuncName = parts[3]
|
||||
stack := vm.Stack()
|
||||
num_items := 3
|
||||
for i := len(stack) - 1; i >= 0 && num_items > 0; i-- {
|
||||
out.Args = append(out.Args, autoQuote(stack[i]))
|
||||
num_items--
|
||||
}
|
||||
outputs = append(outputs, out)
|
||||
//double check OpCallFast and OpCallTyped
|
||||
case "OpCallFast", "OpCallTyped":
|
||||
//
|
||||
case "OpCallN": //Op for function calls with more than 3 args
|
||||
out.Func = true
|
||||
out.FuncName = parts[1]
|
||||
stack := vm.Stack()
|
||||
|
||||
//for OpCallN, we get the number of args
|
||||
if len(program.Arguments) >= ip {
|
||||
nb_args := program.Arguments[ip]
|
||||
if nb_args > 0 {
|
||||
//we need to skip the top item on stack
|
||||
for i := len(stack) - 2; i >= 0 && nb_args > 0; i-- {
|
||||
out.Args = append(out.Args, autoQuote(stack[i]))
|
||||
nb_args--
|
||||
}
|
||||
}
|
||||
} else { //let's blindly take the items on stack
|
||||
for _, val := range vm.Stack() {
|
||||
out.Args = append(out.Args, autoQuote(val))
|
||||
}
|
||||
}
|
||||
outputs = append(outputs, out)
|
||||
case "OpEqualString", "OpEqual", "OpEqualInt": //comparisons
|
||||
stack := vm.Stack()
|
||||
out.Comparison = true
|
||||
out.Left = autoQuote(stack[0])
|
||||
out.Right = autoQuote(stack[1])
|
||||
outputs = append(outputs, out)
|
||||
case "OpIn": //in operator
|
||||
stack := vm.Stack()
|
||||
out.Condition = true
|
||||
out.ConditionIn = true
|
||||
//seems that we tend to receive stack[1] as a map.
|
||||
//it is tempting to use reflect to extract keys, but we end up with an array that doesn't match the initial order
|
||||
//(because of the random order of the map)
|
||||
out.Args = append(out.Args, autoQuote(stack[0]))
|
||||
out.Args = append(out.Args, autoQuote(stack[1]))
|
||||
outputs = append(outputs, out)
|
||||
case "OpContains": //kind OpIn , but reverse
|
||||
stack := vm.Stack()
|
||||
out.Condition = true
|
||||
out.ConditionContains = true
|
||||
//seems that we tend to receive stack[1] as a map.
|
||||
//it is tempting to use reflect to extract keys, but we end up with an array that doesn't match the initial order
|
||||
//(because of the random order of the map)
|
||||
out.Args = append(out.Args, autoQuote(stack[0]))
|
||||
out.Args = append(out.Args, autoQuote(stack[1]))
|
||||
outputs = append(outputs, out)
|
||||
}
|
||||
return outputs, nil
|
||||
}
|
||||
|
||||
func (erp ExprRuntimeDebug) ipSeek(ip int) []string {
|
||||
for i := 0; i < len(erp.Lines); i++ {
|
||||
parts := strings.Split(erp.Lines[i], "\t")
|
||||
if parts[0] == strconv.Itoa(ip) {
|
||||
return parts
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Run(program *vm.Program, env interface{}, logger *log.Entry, debug bool) (any, error) {
|
||||
if debug {
|
||||
dbgInfo, ret, err := RunWithDebug(program, env, logger)
|
||||
DisplayExprDebug(program, dbgInfo, logger, ret)
|
||||
return ret, err
|
||||
}
|
||||
return expr.Run(program, env)
|
||||
}
|
||||
|
||||
func cleanTextForDebug(text string) string {
|
||||
text = strings.Join(strings.Fields(text), " ")
|
||||
text = strings.Trim(text, " \t\n")
|
||||
return text
|
||||
}
|
||||
|
||||
func DisplayExprDebug(program *vm.Program, outputs []OpOutput, logger *log.Entry, ret any) {
|
||||
logger.Debugf("dbg(result=%v): %s", ret, cleanTextForDebug(program.Source.Content()))
|
||||
for _, output := range outputs {
|
||||
logger.Debugf("%s", output.String())
|
||||
}
|
||||
}
|
||||
|
||||
// TBD: Based on the level of the logger (ie. trace vs debug) we could decide to add more low level instructions (pop, push, etc.)
|
||||
func RunWithDebug(program *vm.Program, env interface{}, logger *log.Entry) ([]OpOutput, any, error) {
|
||||
|
||||
var outputs []OpOutput = []OpOutput{}
|
||||
var buf strings.Builder
|
||||
var erp ExprRuntimeDebug = ExprRuntimeDebug{
|
||||
Logger: logger,
|
||||
}
|
||||
var debugErr chan error = make(chan error)
|
||||
vm := vm.Debug()
|
||||
done := false
|
||||
program.Opcodes(&buf)
|
||||
lines := strings.Split(buf.String(), "\n")
|
||||
erp.Lines = lines
|
||||
|
||||
go func() {
|
||||
var err error
|
||||
erp.Logger.Tracef("[START] ip 0")
|
||||
ops := erp.ipSeek(0)
|
||||
if ops == nil {
|
||||
debugErr <- fmt.Errorf("failed getting ops for ip 0")
|
||||
return
|
||||
}
|
||||
if outputs, err = erp.ipDebug(0, vm, program, ops, outputs); err != nil {
|
||||
debugErr <- fmt.Errorf("error while debugging at ip 0")
|
||||
}
|
||||
vm.Step()
|
||||
for ip := range vm.Position() {
|
||||
ops := erp.ipSeek(ip)
|
||||
if ops == nil { //we reached the end of the program, we shouldn't throw an error
|
||||
erp.Logger.Tracef("[DONE] ip %d", ip)
|
||||
debugErr <- nil
|
||||
return
|
||||
}
|
||||
if outputs, err = erp.ipDebug(ip, vm, program, ops, outputs); err != nil {
|
||||
debugErr <- fmt.Errorf("error while debugging at ip %d", ip)
|
||||
return
|
||||
}
|
||||
if done {
|
||||
debugErr <- nil
|
||||
return
|
||||
}
|
||||
vm.Step()
|
||||
}
|
||||
debugErr <- nil
|
||||
}()
|
||||
|
||||
var return_error error
|
||||
ret, err := vm.Run(program, env)
|
||||
done = true
|
||||
//if the expr runtime failed, we don't need to wait for the debug to finish
|
||||
if err != nil {
|
||||
return_error = err
|
||||
} else {
|
||||
err = <-debugErr
|
||||
if err != nil {
|
||||
log.Warningf("error while debugging expr: %s", err)
|
||||
}
|
||||
}
|
||||
//the overall result of expression is the result of last op ?
|
||||
if len(outputs) > 0 {
|
||||
lastOutIdx := len(outputs)
|
||||
if lastOutIdx > 0 {
|
||||
lastOutIdx -= 1
|
||||
}
|
||||
switch val := ret.(type) {
|
||||
case bool:
|
||||
log.Tracef("completing with bool %t", ret)
|
||||
//if outputs[lastOutIdx].Comparison {
|
||||
outputs[lastOutIdx].StrConditionResult = fmt.Sprintf("%v", ret)
|
||||
outputs[lastOutIdx].ConditionResult = new(bool)
|
||||
*outputs[lastOutIdx].ConditionResult = val
|
||||
outputs[lastOutIdx].Finalized = true
|
||||
default:
|
||||
log.Tracef("completing with type %T -> %v", ret, ret)
|
||||
outputs[lastOutIdx].StrConditionResult = fmt.Sprintf("%v", ret)
|
||||
outputs[lastOutIdx].Finalized = true
|
||||
}
|
||||
} else {
|
||||
log.Tracef("no output from expr runtime")
|
||||
}
|
||||
return outputs, ret, return_error
|
||||
}
|
344
pkg/exprhelpers/debugger_test.go
Normal file
344
pkg/exprhelpers/debugger_test.go
Normal file
|
@ -0,0 +1,344 @@
|
|||
package exprhelpers
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/antonmedv/expr"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type ExprDbgTest struct {
|
||||
Name string
|
||||
Expr string
|
||||
ExpectedOutputs []OpOutput
|
||||
ExpectedFailedCompile bool
|
||||
ExpectedFailRuntime bool
|
||||
Env map[string]interface{}
|
||||
LogLevel log.Level
|
||||
}
|
||||
|
||||
// For the sake of testing functions with 2, 3 and N args
|
||||
func UpperTwo(params ...any) (any, error) {
|
||||
s := params[0].(string)
|
||||
v := params[1].(string)
|
||||
return strings.ToUpper(s) + strings.ToUpper(v), nil
|
||||
}
|
||||
|
||||
func UpperThree(params ...any) (any, error) {
|
||||
s := params[0].(string)
|
||||
v := params[1].(string)
|
||||
x := params[2].(string)
|
||||
return strings.ToUpper(s) + strings.ToUpper(v) + strings.ToUpper(x), nil
|
||||
}
|
||||
|
||||
func UpperN(params ...any) (any, error) {
|
||||
s := params[0].(string)
|
||||
v := params[1].(string)
|
||||
x := params[2].(string)
|
||||
y := params[3].(string)
|
||||
return strings.ToUpper(s) + strings.ToUpper(v) + strings.ToUpper(x) + strings.ToUpper(y), nil
|
||||
}
|
||||
|
||||
func boolPtr(b bool) *bool {
|
||||
return &b
|
||||
}
|
||||
|
||||
type teststruct struct {
|
||||
Foo string
|
||||
}
|
||||
|
||||
func TestBaseDbg(t *testing.T) {
|
||||
defaultEnv := map[string]interface{}{
|
||||
"queue": &types.Queue{},
|
||||
"evt": &types.Event{},
|
||||
"sample_array": []string{"a", "b", "c", "ZZ"},
|
||||
"base_string": "hello world",
|
||||
"base_int": 42,
|
||||
"base_float": 42.42,
|
||||
"nillvar": &teststruct{},
|
||||
"base_struct": struct {
|
||||
Foo string
|
||||
Bar int
|
||||
Myarr []string
|
||||
}{
|
||||
Foo: "bar",
|
||||
Bar: 42,
|
||||
Myarr: []string{"a", "b", "c"},
|
||||
},
|
||||
}
|
||||
// tips for the tests:
|
||||
// use '%#v' to dump in golang syntax
|
||||
// use regexp to clear empty/default fields:
|
||||
// [a-z]+: (false|\[\]string\(nil\)|""),
|
||||
//ConditionResult:(*bool)
|
||||
|
||||
//Missing multi parametes function
|
||||
tests := []ExprDbgTest{
|
||||
{
|
||||
Name: "nill deref",
|
||||
Expr: "Upper('1') == '1' && nillvar.Foo == '42'",
|
||||
Env: defaultEnv,
|
||||
ExpectedFailRuntime: true,
|
||||
ExpectedOutputs: []OpOutput{
|
||||
{Code: "Upper('1')", CodeDepth: 0, Func: true, FuncName: "Upper", Args: []string{"\"1\""}, FuncResults: []string{"\"1\""}, ConditionResult: (*bool)(nil), Finalized: true},
|
||||
{Code: "== '1'", CodeDepth: 0, Comparison: true, Left: "\"1\"", Right: "\"1\"", StrConditionResult: "[true]", ConditionResult: boolPtr(true), Finalized: true},
|
||||
{Code: "&&", CodeDepth: 0, JumpIf: true, IfFalse: true, StrConditionResult: "<nil>", ConditionResult: boolPtr(true), Finalized: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "OpCall2",
|
||||
Expr: "UpperTwo('hello', 'world') == 'HELLOWORLD'",
|
||||
Env: defaultEnv,
|
||||
ExpectedOutputs: []OpOutput{
|
||||
{Code: "UpperTwo('hello', 'world')", CodeDepth: 0, Func: true, FuncName: "UpperTwo", Args: []string{"\"world\"", "\"hello\""}, FuncResults: []string{"\"HELLOWORLD\""}, ConditionResult: (*bool)(nil), Finalized: true},
|
||||
{Code: "== 'HELLOWORLD'", CodeDepth: 0, Comparison: true, Left: "\"HELLOWORLD\"", Right: "\"HELLOWORLD\"", StrConditionResult: "true", ConditionResult: boolPtr(true), Finalized: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "OpCall3",
|
||||
Expr: "UpperThree('hello', 'world', 'foo') == 'HELLOWORLDFOO'",
|
||||
Env: defaultEnv,
|
||||
ExpectedOutputs: []OpOutput{
|
||||
{Code: "UpperThree('hello', 'world', 'foo')", CodeDepth: 0, Func: true, FuncName: "UpperThree", Args: []string{"\"foo\"", "\"world\"", "\"hello\""}, FuncResults: []string{"\"HELLOWORLDFOO\""}, ConditionResult: (*bool)(nil), Finalized: true},
|
||||
{Code: "== 'HELLOWORLDFOO'", CodeDepth: 0, Comparison: true, Left: "\"HELLOWORLDFOO\"", Right: "\"HELLOWORLDFOO\"", StrConditionResult: "true", ConditionResult: boolPtr(true), Finalized: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "OpCallN",
|
||||
Expr: "UpperN('hello', 'world', 'foo', 'lol') == UpperN('hello', 'world', 'foo', 'lol')",
|
||||
Env: defaultEnv,
|
||||
ExpectedOutputs: []OpOutput{
|
||||
{Code: "UpperN('hello', 'world', 'foo', 'lol')", CodeDepth: 0, Func: true, FuncName: "OpCallN", Args: []string{"\"lol\"", "\"foo\"", "\"world\"", "\"hello\""}, FuncResults: []string{"\"HELLOWORLDFOOLOL\""}, ConditionResult: (*bool)(nil), Finalized: true},
|
||||
{Code: "UpperN('hello', 'world', 'foo', 'lol')", CodeDepth: 0, Func: true, FuncName: "OpCallN", Args: []string{"\"lol\"", "\"foo\"", "\"world\"", "\"hello\""}, FuncResults: []string{"\"HELLOWORLDFOOLOL\""}, ConditionResult: (*bool)(nil), Finalized: true},
|
||||
{Code: "== UpperN('hello', 'world', 'foo', 'lol')", CodeDepth: 0, Comparison: true, Left: "\"HELLOWORLDFOOLOL\"", Right: "\"HELLOWORLDFOOLOL\"", StrConditionResult: "true", ConditionResult: boolPtr(true), Finalized: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "base string cmp",
|
||||
Expr: "base_string == 'hello world'",
|
||||
Env: defaultEnv,
|
||||
ExpectedOutputs: []OpOutput{
|
||||
{Code: "== 'hello world'", CodeDepth: 0, Comparison: true, Left: "\"hello world\"", Right: "\"hello world\"", StrConditionResult: "true", ConditionResult: boolPtr(true), Finalized: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "loop with func call",
|
||||
Expr: "count(base_struct.Myarr, {Upper(#) == 'C'}) == 1",
|
||||
Env: defaultEnv,
|
||||
ExpectedOutputs: []OpOutput{
|
||||
{Code: "count(base_struct.Myarr, {", CodeDepth: 4, BlockStart: true, ConditionResult: (*bool)(nil), Finalized: false},
|
||||
{Code: "Upper(#)", CodeDepth: 4, Func: true, FuncName: "Upper", Args: []string{"\"a\""}, FuncResults: []string{"\"A\""}, ConditionResult: (*bool)(nil), Finalized: true},
|
||||
{Code: "== 'C'})", CodeDepth: 4, Comparison: true, Left: "\"A\"", Right: "\"C\"", StrConditionResult: "[false]", ConditionResult: boolPtr(false), Finalized: true},
|
||||
{Code: "count(base_struct.Myarr, {Upper(#) == 'C'})", CodeDepth: 4, JumpIf: true, IfFalse: true, StrConditionResult: "false", ConditionResult: boolPtr(false), Finalized: false},
|
||||
{Code: "Upper(#)", CodeDepth: 4, Func: true, FuncName: "Upper", Args: []string{"\"b\""}, FuncResults: []string{"\"B\""}, ConditionResult: (*bool)(nil), Finalized: true},
|
||||
{Code: "== 'C'})", CodeDepth: 4, Comparison: true, Left: "\"B\"", Right: "\"C\"", StrConditionResult: "[false]", ConditionResult: boolPtr(false), Finalized: true},
|
||||
{Code: "count(base_struct.Myarr, {Upper(#) == 'C'})", CodeDepth: 4, JumpIf: true, IfFalse: true, StrConditionResult: "false", ConditionResult: boolPtr(false), Finalized: false},
|
||||
{Code: "Upper(#)", CodeDepth: 4, Func: true, FuncName: "Upper", Args: []string{"\"c\""}, FuncResults: []string{"\"C\""}, ConditionResult: (*bool)(nil), Finalized: true},
|
||||
{Code: "== 'C'})", CodeDepth: 4, Comparison: true, Left: "\"C\"", Right: "\"C\"", StrConditionResult: "[true]", ConditionResult: boolPtr(true), Finalized: true},
|
||||
{Code: "count(base_struct.Myarr, {Upper(#) == 'C'})", CodeDepth: 4, JumpIf: true, IfFalse: true, StrConditionResult: "true", ConditionResult: boolPtr(true), Finalized: false},
|
||||
{Code: "count(base_struct.Myarr, {Upper(#) == 'C'})", CodeDepth: 0, BlockEnd: true, StrConditionResult: "[1]", ConditionResult: (*bool)(nil), Finalized: false},
|
||||
{Code: "== 1", CodeDepth: 0, Comparison: true, Left: "1", Right: "1", StrConditionResult: "true", ConditionResult: boolPtr(true), Finalized: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "loop with func call and extra check",
|
||||
Expr: "count(base_struct.Myarr, {Upper(#) == 'C'}) == 1 && Upper(base_struct.Foo) == 'BAR'",
|
||||
Env: defaultEnv,
|
||||
ExpectedOutputs: []OpOutput{
|
||||
{Code: "count(base_struct.Myarr, {", CodeDepth: 4, BlockStart: true, ConditionResult: (*bool)(nil), Finalized: false},
|
||||
{Code: "Upper(#)", CodeDepth: 4, Func: true, FuncName: "Upper", Args: []string{"\"a\""}, FuncResults: []string{"\"A\""}, ConditionResult: (*bool)(nil), Finalized: true},
|
||||
{Code: "== 'C'})", CodeDepth: 4, Comparison: true, Left: "\"A\"", Right: "\"C\"", StrConditionResult: "[false]", ConditionResult: boolPtr(false), Finalized: true},
|
||||
{Code: "count(base_struct.Myarr, {Upper(#) == 'C'})", CodeDepth: 4, JumpIf: true, IfFalse: true, StrConditionResult: "false", ConditionResult: boolPtr(false), Finalized: false},
|
||||
{Code: "Upper(#)", CodeDepth: 4, Func: true, FuncName: "Upper", Args: []string{"\"b\""}, FuncResults: []string{"\"B\""}, ConditionResult: (*bool)(nil), Finalized: true},
|
||||
{Code: "== 'C'})", CodeDepth: 4, Comparison: true, Left: "\"B\"", Right: "\"C\"", StrConditionResult: "[false]", ConditionResult: boolPtr(false), Finalized: true},
|
||||
{Code: "count(base_struct.Myarr, {Upper(#) == 'C'})", CodeDepth: 4, JumpIf: true, IfFalse: true, StrConditionResult: "false", ConditionResult: boolPtr(false), Finalized: false},
|
||||
{Code: "Upper(#)", CodeDepth: 4, Func: true, FuncName: "Upper", Args: []string{"\"c\""}, FuncResults: []string{"\"C\""}, ConditionResult: (*bool)(nil), Finalized: true},
|
||||
{Code: "== 'C'})", CodeDepth: 4, Comparison: true, Left: "\"C\"", Right: "\"C\"", StrConditionResult: "[true]", ConditionResult: boolPtr(true), Finalized: true},
|
||||
{Code: "count(base_struct.Myarr, {Upper(#) == 'C'})", CodeDepth: 4, JumpIf: true, IfFalse: true, StrConditionResult: "true", ConditionResult: boolPtr(true), Finalized: false},
|
||||
{Code: "count(base_struct.Myarr, {Upper(#) == 'C'})", CodeDepth: 0, BlockEnd: true, StrConditionResult: "[1]", ConditionResult: (*bool)(nil), Finalized: false},
|
||||
{Code: "== 1", CodeDepth: 0, Comparison: true, Left: "1", Right: "1", StrConditionResult: "[true]", ConditionResult: boolPtr(true), Finalized: true},
|
||||
{Code: "&&", CodeDepth: 0, JumpIf: true, IfFalse: true, StrConditionResult: "true", ConditionResult: boolPtr(true), Finalized: false},
|
||||
{Code: "Upper(base_struct.Foo)", CodeDepth: 0, Func: true, FuncName: "Upper", Args: []string{"\"bar\""}, FuncResults: []string{"\"BAR\""}, ConditionResult: (*bool)(nil), Finalized: true},
|
||||
{Code: "== 'BAR'", CodeDepth: 0, Comparison: true, Left: "\"BAR\"", Right: "\"BAR\"", StrConditionResult: "true", ConditionResult: boolPtr(true), Finalized: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "base 'in' test",
|
||||
Expr: "base_int in [1,2,3,4,42]",
|
||||
Env: defaultEnv,
|
||||
ExpectedOutputs: []OpOutput{
|
||||
{Code: "in [1,2,3,4,42]", CodeDepth: 0, Args: []string{"42", "map[1:{} 2:{} 3:{} 4:{} 42:{}]"}, Condition: true, ConditionIn: true, StrConditionResult: "true", ConditionResult: boolPtr(true), Finalized: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "base string cmp",
|
||||
Expr: "base_string == 'hello world'",
|
||||
Env: defaultEnv,
|
||||
ExpectedOutputs: []OpOutput{
|
||||
{Code: "== 'hello world'", CodeDepth: 0, Comparison: true, Left: "\"hello world\"", Right: "\"hello world\"", StrConditionResult: "true", ConditionResult: boolPtr(true), Finalized: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "base int cmp",
|
||||
Expr: "base_int == 42",
|
||||
Env: defaultEnv,
|
||||
ExpectedOutputs: []OpOutput{
|
||||
{Code: "== 42", CodeDepth: 0, Comparison: true, Left: "42", Right: "42", StrConditionResult: "true", ConditionResult: boolPtr(true), Finalized: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "negative check",
|
||||
Expr: "base_int != 43",
|
||||
Env: defaultEnv,
|
||||
ExpectedOutputs: []OpOutput{
|
||||
{Code: "!= 43", CodeDepth: 0, Negated: true, Comparison: true, Left: "42", Right: "43", StrConditionResult: "true", ConditionResult: boolPtr(true), Finalized: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "testing ORs",
|
||||
Expr: "base_int == 43 || base_int == 42",
|
||||
Env: defaultEnv,
|
||||
ExpectedOutputs: []OpOutput{
|
||||
{Code: "== 43", CodeDepth: 0, Comparison: true, Left: "42", Right: "43", StrConditionResult: "[false]", ConditionResult: boolPtr(false), Finalized: true},
|
||||
{Code: "||", CodeDepth: 0, JumpIf: true, IfTrue: true, StrConditionResult: "false", ConditionResult: boolPtr(false), Finalized: false},
|
||||
{Code: "== 42", CodeDepth: 0, Comparison: true, Left: "42", Right: "42", StrConditionResult: "true", ConditionResult: boolPtr(true), Finalized: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "testing basic true",
|
||||
Expr: "true",
|
||||
Env: defaultEnv,
|
||||
ExpectedOutputs: []OpOutput{
|
||||
{Code: "true", CodeDepth: 0, Condition: true, StrConditionResult: "true", ConditionResult: boolPtr(true), Finalized: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "testing basic false",
|
||||
Expr: "false",
|
||||
Env: defaultEnv,
|
||||
ExpectedOutputs: []OpOutput{
|
||||
{Code: "false", CodeDepth: 0, Condition: true, StrConditionResult: "false", ConditionResult: boolPtr(false), Finalized: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "testing multi lines",
|
||||
Expr: `base_int == 42 &&
|
||||
base_string == 'hello world' &&
|
||||
(base_struct.Bar == 41 || base_struct.Bar == 42)`,
|
||||
Env: defaultEnv,
|
||||
ExpectedOutputs: []OpOutput{
|
||||
{Code: "== 42", CodeDepth: 0, Comparison: true, Left: "42", Right: "42", StrConditionResult: "[true]", ConditionResult: boolPtr(true), Finalized: true},
|
||||
{Code: "&&", CodeDepth: 0, JumpIf: true, IfFalse: true, StrConditionResult: "true", ConditionResult: boolPtr(true), Finalized: false},
|
||||
{Code: "== 'hello world'", CodeDepth: 0, Comparison: true, Left: "\"hello world\"", Right: "\"hello world\"", StrConditionResult: "[true]", ConditionResult: boolPtr(true), Finalized: true},
|
||||
{Code: "&& (", CodeDepth: 0, JumpIf: true, IfFalse: true, StrConditionResult: "true", ConditionResult: boolPtr(true), Finalized: false},
|
||||
{Code: "== 41", CodeDepth: 0, Comparison: true, Left: "42", Right: "41", StrConditionResult: "[false]", ConditionResult: boolPtr(false), Finalized: true},
|
||||
{Code: "||", CodeDepth: 0, JumpIf: true, IfTrue: true, StrConditionResult: "false", ConditionResult: boolPtr(false), Finalized: false},
|
||||
{Code: "== 42)", CodeDepth: 0, Comparison: true, Left: "42", Right: "42", StrConditionResult: "true", ConditionResult: boolPtr(true), Finalized: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "upper + in",
|
||||
Expr: "Upper(base_string) contains Upper('wOrlD')",
|
||||
Env: defaultEnv,
|
||||
ExpectedOutputs: []OpOutput{
|
||||
{Code: "Upper(base_string)", CodeDepth: 0, Func: true, FuncName: "Upper", Args: []string{"\"hello world\""}, FuncResults: []string{"\"HELLO WORLD\""}, ConditionResult: (*bool)(nil), Finalized: true},
|
||||
{Code: "Upper('wOrlD')", CodeDepth: 0, Func: true, FuncName: "Upper", Args: []string{"\"wOrlD\""}, FuncResults: []string{"\"WORLD\""}, ConditionResult: (*bool)(nil), Finalized: true},
|
||||
{Code: "contains Upper('wOrlD')", CodeDepth: 0, Args: []string{"\"HELLO WORLD\"", "\"WORLD\""}, Condition: true, ConditionContains: true, StrConditionResult: "true", ConditionResult: boolPtr(true), Finalized: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "upper + complex",
|
||||
Expr: `( Upper(base_string) contains Upper('/someurl?x=1') ||
|
||||
Upper(base_string) contains Upper('/someotherurl?account-name=admin&account-status=1&ow=cmd') )
|
||||
and base_string startsWith ('40') and Upper(base_string) == 'POST'`,
|
||||
Env: defaultEnv,
|
||||
ExpectedOutputs: []OpOutput{
|
||||
{Code: "Upper(base_string)", CodeDepth: 0, Func: true, FuncName: "Upper", Args: []string{"\"hello world\""}, FuncResults: []string{"\"HELLO WORLD\""}, ConditionResult: (*bool)(nil), Finalized: true},
|
||||
{Code: "Upper('/someurl?x=1')", CodeDepth: 0, Func: true, FuncName: "Upper", Args: []string{"\"/someurl?x=1\""}, FuncResults: []string{"\"/SOMEURL?X=1\""}, ConditionResult: (*bool)(nil), Finalized: true},
|
||||
{Code: "contains Upper('/someurl?x=1')", CodeDepth: 0, Args: []string{"\"HELLO WORLD\"", "\"/SOMEURL?X=1\""}, Condition: true, ConditionContains: true, StrConditionResult: "[false]", ConditionResult: boolPtr(false), Finalized: true},
|
||||
{Code: "||", CodeDepth: 0, JumpIf: true, IfTrue: true, StrConditionResult: "false", ConditionResult: boolPtr(false), Finalized: false},
|
||||
{Code: "Upper(base_string)", CodeDepth: 0, Func: true, FuncName: "Upper", Args: []string{"\"hello world\""}, FuncResults: []string{"\"HELLO WORLD\""}, ConditionResult: (*bool)(nil), Finalized: true},
|
||||
{Code: "Upper('/someotherurl?account-name=admin&account-status=1&ow=cmd') )", CodeDepth: 0, Func: true, FuncName: "Upper", Args: []string{"\"/someotherurl?account-name=admin&account...\""}, FuncResults: []string{"\"/SOMEOTHERURL?ACCOUNT-NAME=ADMIN&ACCOUNT...\""}, ConditionResult: (*bool)(nil), Finalized: true},
|
||||
{Code: "contains Upper('/someotherurl?account-name=admin&account-status=1&ow=cmd') )", CodeDepth: 0, Args: []string{"\"HELLO WORLD\"", "\"/SOMEOTHERURL?ACCOUNT-NAME=ADMIN&ACCOUNT...\""}, Condition: true, ConditionContains: true, StrConditionResult: "[false]", ConditionResult: boolPtr(false), Finalized: true},
|
||||
{Code: "and", CodeDepth: 0, JumpIf: true, IfFalse: true, StrConditionResult: "false", ConditionResult: boolPtr(false), Finalized: false},
|
||||
{Code: "and", CodeDepth: 0, JumpIf: true, IfFalse: true, StrConditionResult: "false", ConditionResult: boolPtr(false), Finalized: true},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
logger := log.WithField("test", "exprhelpers")
|
||||
for _, test := range tests {
|
||||
if test.LogLevel != 0 {
|
||||
log.SetLevel(test.LogLevel)
|
||||
} else {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
}
|
||||
|
||||
extraFuncs := []expr.Option{}
|
||||
extraFuncs = append(extraFuncs,
|
||||
expr.Function("UpperTwo",
|
||||
UpperTwo,
|
||||
[]interface{}{new(func(string, string) string)}...,
|
||||
))
|
||||
extraFuncs = append(extraFuncs,
|
||||
expr.Function("UpperThree",
|
||||
UpperThree,
|
||||
[]interface{}{new(func(string, string, string) string)}...,
|
||||
))
|
||||
extraFuncs = append(extraFuncs,
|
||||
expr.Function("UpperN",
|
||||
UpperN,
|
||||
[]interface{}{new(func(string, string, string, string) string)}...,
|
||||
))
|
||||
supaEnv := GetExprOptions(test.Env)
|
||||
supaEnv = append(supaEnv, extraFuncs...)
|
||||
|
||||
prog, err := expr.Compile(test.Expr, supaEnv...)
|
||||
if test.ExpectedFailedCompile {
|
||||
if err == nil {
|
||||
t.Fatalf("test %s : expected compile error", test.Name)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Fatalf("test %s : unexpected compile error : %s", test.Name, err)
|
||||
}
|
||||
}
|
||||
if test.Name == "nill deref" {
|
||||
test.Env["nillvar"] = nil
|
||||
}
|
||||
outdbg, ret, err := RunWithDebug(prog, test.Env, logger)
|
||||
if test.ExpectedFailRuntime {
|
||||
if err == nil {
|
||||
t.Fatalf("test %s : expected runtime error", test.Name)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Fatalf("test %s : unexpected runtime error : %s", test.Name, err)
|
||||
}
|
||||
}
|
||||
log.SetLevel(log.DebugLevel)
|
||||
DisplayExprDebug(prog, outdbg, logger, ret)
|
||||
if len(outdbg) != len(test.ExpectedOutputs) {
|
||||
t.Errorf("failed test %s", test.Name)
|
||||
t.Errorf("%#v", outdbg)
|
||||
//out, _ := yaml.Marshal(outdbg)
|
||||
//fmt.Printf("%s", string(out))
|
||||
t.Fatalf("test %s : expected %d outputs, got %d", test.Name, len(test.ExpectedOutputs), len(outdbg))
|
||||
|
||||
}
|
||||
for i, out := range outdbg {
|
||||
if !reflect.DeepEqual(out, test.ExpectedOutputs[i]) {
|
||||
spew.Config.DisableMethods = true
|
||||
t.Errorf("failed test %s", test.Name)
|
||||
t.Errorf("expected : %#v", test.ExpectedOutputs[i])
|
||||
t.Errorf("got : %#v", out)
|
||||
t.Fatalf("%d/%d : mismatch", i, len(outdbg))
|
||||
}
|
||||
//DisplayExprDebug(prog, outdbg, logger, ret)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -97,19 +97,12 @@ func TestVisitor(t *testing.T) {
|
|||
}
|
||||
|
||||
log.SetLevel(log.DebugLevel)
|
||||
clog := log.WithFields(log.Fields{
|
||||
"type": "test",
|
||||
})
|
||||
|
||||
for _, test := range tests {
|
||||
compiledFilter, err := expr.Compile(test.filter, GetExprOptions(test.env)...)
|
||||
if err != nil && test.err == nil {
|
||||
log.Fatalf("compile: %s", err)
|
||||
}
|
||||
debugFilter, err := NewDebugger(test.filter, GetExprOptions(test.env)...)
|
||||
if err != nil && test.err == nil {
|
||||
log.Fatalf("debug: %s", err)
|
||||
}
|
||||
|
||||
if compiledFilter != nil {
|
||||
result, err := expr.Run(compiledFilter, test.env)
|
||||
|
@ -121,9 +114,6 @@ func TestVisitor(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
if debugFilter != nil {
|
||||
debugFilter.Run(clog, test.result, test.env)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,160 +0,0 @@
|
|||
package exprhelpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/antonmedv/expr/parser"
|
||||
"github.com/google/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/antonmedv/expr"
|
||||
"github.com/antonmedv/expr/ast"
|
||||
"github.com/antonmedv/expr/vm"
|
||||
)
|
||||
|
||||
/*
|
||||
Visitor is used to reconstruct variables with its property called in an expr filter
|
||||
Thus, we can debug expr filter by displaying all variables contents present in the filter
|
||||
*/
|
||||
type visitor struct {
|
||||
newVar bool
|
||||
currentId string
|
||||
vars map[string][]string
|
||||
logger *log.Entry
|
||||
}
|
||||
|
||||
func (v *visitor) Visit(node *ast.Node) {
|
||||
switch n := (*node).(type) {
|
||||
case *ast.IdentifierNode:
|
||||
v.newVar = true
|
||||
uid, _ := uuid.NewUUID()
|
||||
v.currentId = uid.String()
|
||||
v.vars[v.currentId] = []string{n.Value}
|
||||
case *ast.MemberNode:
|
||||
if n2, ok := n.Property.(*ast.StringNode); ok {
|
||||
v.vars[v.currentId] = append(v.vars[v.currentId], n2.Value)
|
||||
}
|
||||
case *ast.StringNode: //Don't reset here, as any attribute of a member node is a string node (in evt.X, evt is member node, X is string node)
|
||||
default:
|
||||
v.newVar = false
|
||||
v.currentId = ""
|
||||
/*case *ast.IntegerNode:
|
||||
v.logger.Infof("integer node found: %+v", n)
|
||||
case *ast.FloatNode:
|
||||
v.logger.Infof("float node found: %+v", n)
|
||||
case *ast.BoolNode:
|
||||
v.logger.Infof("boolean node found: %+v", n)
|
||||
case *ast.ArrayNode:
|
||||
v.logger.Infof("array node found: %+v", n)
|
||||
case *ast.ConstantNode:
|
||||
v.logger.Infof("constant node found: %+v", n)
|
||||
case *ast.UnaryNode:
|
||||
v.logger.Infof("unary node found: %+v", n)
|
||||
case *ast.BinaryNode:
|
||||
v.logger.Infof("binary node found: %+v", n)
|
||||
case *ast.CallNode:
|
||||
v.logger.Infof("call node found: %+v", n)
|
||||
case *ast.BuiltinNode:
|
||||
v.logger.Infof("builtin node found: %+v", n)
|
||||
case *ast.ConditionalNode:
|
||||
v.logger.Infof("conditional node found: %+v", n)
|
||||
case *ast.ChainNode:
|
||||
v.logger.Infof("chain node found: %+v", n)
|
||||
case *ast.PairNode:
|
||||
v.logger.Infof("pair node found: %+v", n)
|
||||
case *ast.MapNode:
|
||||
v.logger.Infof("map node found: %+v", n)
|
||||
case *ast.SliceNode:
|
||||
v.logger.Infof("slice node found: %+v", n)
|
||||
case *ast.ClosureNode:
|
||||
v.logger.Infof("closure node found: %+v", n)
|
||||
case *ast.PointerNode:
|
||||
v.logger.Infof("pointer node found: %+v", n)
|
||||
default:
|
||||
v.logger.Infof("unknown node found: %+v | type: %T", n, n)*/
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Build reconstruct all the variables used in a filter (to display their content later).
|
||||
*/
|
||||
func (v *visitor) Build(filter string, exprEnv ...expr.Option) (*ExprDebugger, error) {
|
||||
var expressions []*expression
|
||||
ret := &ExprDebugger{
|
||||
filter: filter,
|
||||
}
|
||||
if filter == "" {
|
||||
v.logger.Debugf("unable to create expr debugger with empty filter")
|
||||
return &ExprDebugger{}, nil
|
||||
}
|
||||
v.newVar = false
|
||||
v.vars = make(map[string][]string)
|
||||
tree, err := parser.Parse(filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ast.Walk(&tree.Node, v)
|
||||
log.Debugf("vars: %+v", v.vars)
|
||||
|
||||
for _, variable := range v.vars {
|
||||
if variable[0] != "evt" {
|
||||
continue
|
||||
}
|
||||
toBuild := strings.Join(variable, ".")
|
||||
v.logger.Debugf("compiling expression '%s'", toBuild)
|
||||
debugFilter, err := expr.Compile(toBuild, exprEnv...)
|
||||
if err != nil {
|
||||
return ret, fmt.Errorf("compilation of variable '%s' failed: %v", toBuild, err)
|
||||
}
|
||||
tmpExpression := &expression{
|
||||
toBuild,
|
||||
debugFilter,
|
||||
}
|
||||
expressions = append(expressions, tmpExpression)
|
||||
|
||||
}
|
||||
ret.expression = expressions
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// ExprDebugger contains the list of expression to be run when debugging an expression filter
|
||||
type ExprDebugger struct {
|
||||
filter string
|
||||
expression []*expression
|
||||
}
|
||||
|
||||
// expression is the structure that represents the variable in string and compiled format
|
||||
type expression struct {
|
||||
Str string
|
||||
Compiled *vm.Program
|
||||
}
|
||||
|
||||
/*
|
||||
Run display the content of each variable of a filter by evaluating them with expr,
|
||||
again the expr environment given in parameter
|
||||
*/
|
||||
func (e *ExprDebugger) Run(logger *log.Entry, filterResult bool, exprEnv map[string]interface{}) {
|
||||
if len(e.expression) == 0 {
|
||||
logger.Tracef("no variable to eval for filter '%s'", e.filter)
|
||||
return
|
||||
}
|
||||
logger.Debugf("eval(%s) = %s", e.filter, strings.ToUpper(strconv.FormatBool(filterResult)))
|
||||
logger.Debugf("eval variables:")
|
||||
for _, expression := range e.expression {
|
||||
debug, err := expr.Run(expression.Compiled, exprEnv)
|
||||
if err != nil {
|
||||
logger.Errorf("unable to print debug expression for '%s': %s", expression.Str, err)
|
||||
}
|
||||
logger.Debugf(" %s = '%v'", expression.Str, debug)
|
||||
}
|
||||
}
|
||||
|
||||
// NewDebugger is the exported function that build the debuggers expressions
|
||||
func NewDebugger(filter string, exprEnv ...expr.Option) (*ExprDebugger, error) {
|
||||
logger := log.WithField("component", "expr-debugger")
|
||||
visitor := &visitor{logger: logger}
|
||||
exprDebugger, err := visitor.Build(filter, exprEnv...)
|
||||
return exprDebugger, err
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
package exprhelpers
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/antonmedv/expr"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func TestVisitorBuild(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
expr string
|
||||
want []string
|
||||
env map[string]interface{}
|
||||
}{
|
||||
{
|
||||
name: "simple",
|
||||
expr: "evt.X",
|
||||
want: []string{"evt.X"},
|
||||
env: map[string]interface{}{
|
||||
"evt": map[string]interface{}{
|
||||
"X": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "two vars",
|
||||
expr: "evt.X && evt.Y",
|
||||
want: []string{"evt.X", "evt.Y"},
|
||||
env: map[string]interface{}{
|
||||
"evt": map[string]interface{}{
|
||||
"X": 1,
|
||||
"Y": 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "in",
|
||||
expr: "evt.X in [1,2,3]",
|
||||
want: []string{"evt.X"},
|
||||
env: map[string]interface{}{
|
||||
"evt": map[string]interface{}{
|
||||
"X": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "in complex",
|
||||
expr: "evt.X in [1,2,3] && evt.Y in [1,2,3] || evt.Z in [1,2,3]",
|
||||
want: []string{"evt.X", "evt.Y", "evt.Z"},
|
||||
env: map[string]interface{}{
|
||||
"evt": map[string]interface{}{
|
||||
"X": 1,
|
||||
"Y": 2,
|
||||
"Z": 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "function call",
|
||||
expr: "Foo(evt.X, 'ads')",
|
||||
want: []string{"evt.X"},
|
||||
env: map[string]interface{}{
|
||||
"evt": map[string]interface{}{
|
||||
"X": 1,
|
||||
},
|
||||
"Foo": func(x int, y string) int {
|
||||
return x
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
v := &visitor{logger: log.NewEntry(log.New())}
|
||||
ret, err := v.Build(tt.expr, expr.Env(tt.env))
|
||||
if err != nil {
|
||||
t.Errorf("visitor.Build() error = %v", err)
|
||||
return
|
||||
}
|
||||
if len(ret.expression) != len(tt.want) {
|
||||
t.Errorf("visitor.Build() = %v, want %v", ret.expression, tt.want)
|
||||
}
|
||||
//Sort both slices as the order is not guaranteed ??
|
||||
sort.Slice(tt.want, func(i, j int) bool {
|
||||
return tt.want[i] < tt.want[j]
|
||||
})
|
||||
sort.Slice(ret.expression, func(i, j int) bool {
|
||||
return ret.expression[i].Str < ret.expression[j].Str
|
||||
})
|
||||
for idx, v := range ret.expression {
|
||||
if v.Str != tt.want[idx] {
|
||||
t.Errorf("visitor.Build() = %v, want %v", v.Str, tt.want[idx])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -633,6 +633,57 @@ func (t *HubTestItem) RunWithLogFile() error {
|
|||
return fmt.Errorf("can't get current directory: %+v", err)
|
||||
}
|
||||
|
||||
// create runtime folder
|
||||
if err = os.MkdirAll(t.RuntimePath, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("unable to create folder '%s': %+v", t.RuntimePath, err)
|
||||
}
|
||||
|
||||
// create runtime data folder
|
||||
if err = os.MkdirAll(t.RuntimeDataPath, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("unable to create folder '%s': %+v", t.RuntimeDataPath, err)
|
||||
}
|
||||
|
||||
// create runtime hub folder
|
||||
if err = os.MkdirAll(t.RuntimeHubPath, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("unable to create folder '%s': %+v", t.RuntimeHubPath, err)
|
||||
}
|
||||
|
||||
if err = Copy(t.HubIndexFile, filepath.Join(t.RuntimeHubPath, ".index.json")); err != nil {
|
||||
return fmt.Errorf("unable to copy .index.json file in '%s': %s", filepath.Join(t.RuntimeHubPath, ".index.json"), err)
|
||||
}
|
||||
|
||||
// create results folder
|
||||
if err = os.MkdirAll(t.ResultsPath, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("unable to create folder '%s': %+v", t.ResultsPath, err)
|
||||
}
|
||||
|
||||
// copy template config file to runtime folder
|
||||
if err = Copy(t.TemplateConfigPath, t.RuntimeConfigFilePath); err != nil {
|
||||
return fmt.Errorf("unable to copy '%s' to '%s': %v", t.TemplateConfigPath, t.RuntimeConfigFilePath, err)
|
||||
}
|
||||
|
||||
// copy template profile file to runtime folder
|
||||
if err = Copy(t.TemplateProfilePath, t.RuntimeProfileFilePath); err != nil {
|
||||
return fmt.Errorf("unable to copy '%s' to '%s': %v", t.TemplateProfilePath, t.RuntimeProfileFilePath, err)
|
||||
}
|
||||
|
||||
// copy template simulation file to runtime folder
|
||||
if err = Copy(t.TemplateSimulationPath, t.RuntimeSimulationFilePath); err != nil {
|
||||
return fmt.Errorf("unable to copy '%s' to '%s': %v", t.TemplateSimulationPath, t.RuntimeSimulationFilePath, err)
|
||||
}
|
||||
|
||||
crowdsecPatternsFolder := csconfig.DefaultConfigPath("patterns")
|
||||
|
||||
// copy template patterns folder to runtime folder
|
||||
if err = CopyDir(crowdsecPatternsFolder, t.RuntimePatternsPath); err != nil {
|
||||
return fmt.Errorf("unable to copy 'patterns' from '%s' to '%s': %s", crowdsecPatternsFolder, t.RuntimePatternsPath, err)
|
||||
}
|
||||
|
||||
// install the hub in the runtime folder
|
||||
if err = t.InstallHub(); err != nil {
|
||||
return fmt.Errorf("unable to install hub in '%s': %s", t.RuntimeHubPath, err)
|
||||
}
|
||||
|
||||
logFile := t.Config.LogFile
|
||||
logType := t.Config.LogType
|
||||
dsn := fmt.Sprintf("file://%s", logFile)
|
||||
|
|
|
@ -107,7 +107,7 @@ func (b *BayesianEvent) bayesianUpdate(c *BayesianBucket, msg types.Event, l *Le
|
|||
}
|
||||
|
||||
l.logger.Debugf("running condition expression: %s", b.rawCondition.ConditionalFilterName)
|
||||
ret, err := expr.Run(b.conditionalFilterRuntime, map[string]interface{}{"evt": &msg, "queue": l.Queue, "leaky": l})
|
||||
ret, err := exprhelpers.Run(b.conditionalFilterRuntime, map[string]interface{}{"evt": &msg, "queue": l.Queue, "leaky": l}, l.logger, l.BucketConfig.Debug)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to run conditional filter: %s", err)
|
||||
}
|
||||
|
@ -151,7 +151,7 @@ func (b *BayesianEvent) compileCondition() error {
|
|||
|
||||
conditionalExprCacheLock.Unlock()
|
||||
//release the lock during compile same as coditional bucket
|
||||
compiledExpr, err = expr.Compile(b.rawCondition.ConditionalFilterName, exprhelpers.GetExprOptions(map[string]interface{}{"queue": &Queue{}, "leaky": &Leaky{}, "evt": &types.Event{}})...)
|
||||
compiledExpr, err = expr.Compile(b.rawCondition.ConditionalFilterName, exprhelpers.GetExprOptions(map[string]interface{}{"queue": &types.Queue{}, "leaky": &Leaky{}, "evt": &types.Event{}})...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("bayesian condition compile error: %w", err)
|
||||
}
|
||||
|
|
|
@ -31,8 +31,8 @@ func NewBlackhole(bucketFactory *BucketFactory) (*Blackhole, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (bl *Blackhole) OnBucketOverflow(bucketFactory *BucketFactory) func(*Leaky, types.RuntimeAlert, *Queue) (types.RuntimeAlert, *Queue) {
|
||||
return func(leaky *Leaky, alert types.RuntimeAlert, queue *Queue) (types.RuntimeAlert, *Queue) {
|
||||
func (bl *Blackhole) OnBucketOverflow(bucketFactory *BucketFactory) func(*Leaky, types.RuntimeAlert, *types.Queue) (types.RuntimeAlert, *types.Queue) {
|
||||
return func(leaky *Leaky, alert types.RuntimeAlert, queue *types.Queue) (types.RuntimeAlert, *types.Queue) {
|
||||
var blackholed = false
|
||||
var tmp []HiddenKey
|
||||
// search if we are blackholed and refresh the slice
|
||||
|
|
|
@ -31,11 +31,11 @@ type Leaky struct {
|
|||
Limiter rate.RateLimiter `json:"-"`
|
||||
SerializedState rate.Lstate
|
||||
//Queue is used to hold the cache of objects in the bucket, it is used to know 'how many' objects we have in buffer.
|
||||
Queue *Queue
|
||||
Queue *types.Queue
|
||||
//Leaky buckets are receiving message through a chan
|
||||
In chan *types.Event `json:"-"`
|
||||
//Leaky buckets are pushing their overflows through a chan
|
||||
Out chan *Queue `json:"-"`
|
||||
Out chan *types.Queue `json:"-"`
|
||||
// shared for all buckets (the idea is to kill this afterward)
|
||||
AllOut chan types.Event `json:"-"`
|
||||
//max capacity (for burst)
|
||||
|
@ -159,9 +159,9 @@ func FromFactory(bucketFactory BucketFactory) *Leaky {
|
|||
Name: bucketFactory.Name,
|
||||
Limiter: limiter,
|
||||
Uuid: seed.Generate(),
|
||||
Queue: NewQueue(Qsize),
|
||||
Queue: types.NewQueue(Qsize),
|
||||
CacheSize: bucketFactory.CacheSize,
|
||||
Out: make(chan *Queue, 1),
|
||||
Out: make(chan *types.Queue, 1),
|
||||
Suicide: make(chan bool, 1),
|
||||
AllOut: bucketFactory.ret,
|
||||
Capacity: bucketFactory.Capacity,
|
||||
|
@ -374,7 +374,7 @@ func Pour(leaky *Leaky, msg types.Event) {
|
|||
}
|
||||
}
|
||||
|
||||
func (leaky *Leaky) overflow(ofw *Queue) {
|
||||
func (leaky *Leaky) overflow(ofw *types.Queue) {
|
||||
close(leaky.Signal)
|
||||
alert, err := NewAlert(leaky, ofw)
|
||||
if err != nil {
|
||||
|
|
|
@ -33,7 +33,7 @@ func (c *ConditionalOverflow) OnBucketInit(g *BucketFactory) error {
|
|||
} else {
|
||||
conditionalExprCacheLock.Unlock()
|
||||
//release the lock during compile
|
||||
compiledExpr, err = expr.Compile(g.ConditionalOverflow, exprhelpers.GetExprOptions(map[string]interface{}{"queue": &Queue{}, "leaky": &Leaky{}, "evt": &types.Event{}})...)
|
||||
compiledExpr, err = expr.Compile(g.ConditionalOverflow, exprhelpers.GetExprOptions(map[string]interface{}{"queue": &types.Queue{}, "leaky": &Leaky{}, "evt": &types.Event{}})...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("conditional compile error : %w", err)
|
||||
}
|
||||
|
@ -50,12 +50,14 @@ func (c *ConditionalOverflow) AfterBucketPour(b *BucketFactory) func(types.Event
|
|||
var condition, ok bool
|
||||
if c.ConditionalFilterRuntime != nil {
|
||||
l.logger.Debugf("Running condition expression : %s", c.ConditionalFilter)
|
||||
ret, err := expr.Run(c.ConditionalFilterRuntime, map[string]interface{}{"evt": &msg, "queue": l.Queue, "leaky": l})
|
||||
|
||||
ret, err := exprhelpers.Run(c.ConditionalFilterRuntime,
|
||||
map[string]interface{}{"evt": &msg, "queue": l.Queue, "leaky": l},
|
||||
l.logger, b.Debug)
|
||||
if err != nil {
|
||||
l.logger.Errorf("unable to run conditional filter : %s", err)
|
||||
return &msg
|
||||
}
|
||||
|
||||
l.logger.Debugf("Conditional bucket expression returned : %v", ret)
|
||||
|
||||
if condition, ok = ret.(bool); !ok {
|
||||
|
|
|
@ -30,50 +30,49 @@ import (
|
|||
// BucketFactory struct holds all fields for any bucket configuration. This is to have a
|
||||
// generic struct for buckets. This can be seen as a bucket factory.
|
||||
type BucketFactory struct {
|
||||
FormatVersion string `yaml:"format"`
|
||||
Author string `yaml:"author"`
|
||||
Description string `yaml:"description"`
|
||||
References []string `yaml:"references"`
|
||||
Type string `yaml:"type"` //Type can be : leaky, counter, trigger. It determines the main bucket characteristics
|
||||
Name string `yaml:"name"` //Name of the bucket, used later in log and user-messages. Should be unique
|
||||
Capacity int `yaml:"capacity"` //Capacity is applicable to leaky buckets and determines the "burst" capacity
|
||||
LeakSpeed string `yaml:"leakspeed"` //Leakspeed is a float representing how many events per second leak out of the bucket
|
||||
Duration string `yaml:"duration"` //Duration allows 'counter' buckets to have a fixed life-time
|
||||
Filter string `yaml:"filter"` //Filter is an expr that determines if an event is elligible for said bucket. Filter is evaluated against the Event struct
|
||||
GroupBy string `yaml:"groupby,omitempty"` //groupy is an expr that allows to determine the partitions of the bucket. A common example is the source_ip
|
||||
Distinct string `yaml:"distinct"` //Distinct, when present, adds a `Pour()` processor that will only pour uniq items (based on distinct expr result)
|
||||
Debug bool `yaml:"debug"` //Debug, when set to true, will enable debugging for _this_ scenario specifically
|
||||
Labels map[string]interface{} `yaml:"labels"` //Labels is K:V list aiming at providing context the overflow
|
||||
Blackhole string `yaml:"blackhole,omitempty"` //Blackhole is a duration that, if present, will prevent same bucket partition to overflow more often than $duration
|
||||
logger *log.Entry `yaml:"-"` //logger is bucket-specific logger (used by Debug as well)
|
||||
Reprocess bool `yaml:"reprocess"` //Reprocess, if true, will for the bucket to be re-injected into processing chain
|
||||
CacheSize int `yaml:"cache_size"` //CacheSize, if > 0, limits the size of in-memory cache of the bucket
|
||||
Profiling bool `yaml:"profiling"` //Profiling, if true, will make the bucket record pours/overflows/etc.
|
||||
OverflowFilter string `yaml:"overflow_filter"` //OverflowFilter if present, is a filter that must return true for the overflow to go through
|
||||
ConditionalOverflow string `yaml:"condition"` //condition if present, is an expression that must return true for the bucket to overflow
|
||||
BayesianPrior float32 `yaml:"bayesian_prior"`
|
||||
BayesianThreshold float32 `yaml:"bayesian_threshold"`
|
||||
BayesianConditions []RawBayesianCondition `yaml:"bayesian_conditions"` //conditions for the bayesian bucket
|
||||
ScopeType types.ScopeType `yaml:"scope,omitempty"` //to enforce a different remediation than blocking an IP. Will default this to IP
|
||||
BucketName string `yaml:"-"`
|
||||
Filename string `yaml:"-"`
|
||||
RunTimeFilter *vm.Program `json:"-"`
|
||||
ExprDebugger *exprhelpers.ExprDebugger `yaml:"-" json:"-"` // used to debug expression by printing the content of each variable of the expression
|
||||
RunTimeGroupBy *vm.Program `json:"-"`
|
||||
Data []*types.DataSource `yaml:"data,omitempty"`
|
||||
DataDir string `yaml:"-"`
|
||||
CancelOnFilter string `yaml:"cancel_on,omitempty"` //a filter that, if matched, kills the bucket
|
||||
leakspeed time.Duration //internal representation of `Leakspeed`
|
||||
duration time.Duration //internal representation of `Duration`
|
||||
ret chan types.Event //the bucket-specific output chan for overflows
|
||||
processors []Processor //processors is the list of hooks for pour/overflow/create (cf. uniq, blackhole etc.)
|
||||
output bool //??
|
||||
ScenarioVersion string `yaml:"version,omitempty"`
|
||||
hash string `yaml:"-"`
|
||||
Simulated bool `yaml:"simulated"` //Set to true if the scenario instantiating the bucket was in the exclusion list
|
||||
tomb *tomb.Tomb `yaml:"-"`
|
||||
wgPour *sync.WaitGroup `yaml:"-"`
|
||||
wgDumpState *sync.WaitGroup `yaml:"-"`
|
||||
FormatVersion string `yaml:"format"`
|
||||
Author string `yaml:"author"`
|
||||
Description string `yaml:"description"`
|
||||
References []string `yaml:"references"`
|
||||
Type string `yaml:"type"` //Type can be : leaky, counter, trigger. It determines the main bucket characteristics
|
||||
Name string `yaml:"name"` //Name of the bucket, used later in log and user-messages. Should be unique
|
||||
Capacity int `yaml:"capacity"` //Capacity is applicable to leaky buckets and determines the "burst" capacity
|
||||
LeakSpeed string `yaml:"leakspeed"` //Leakspeed is a float representing how many events per second leak out of the bucket
|
||||
Duration string `yaml:"duration"` //Duration allows 'counter' buckets to have a fixed life-time
|
||||
Filter string `yaml:"filter"` //Filter is an expr that determines if an event is elligible for said bucket. Filter is evaluated against the Event struct
|
||||
GroupBy string `yaml:"groupby,omitempty"` //groupy is an expr that allows to determine the partitions of the bucket. A common example is the source_ip
|
||||
Distinct string `yaml:"distinct"` //Distinct, when present, adds a `Pour()` processor that will only pour uniq items (based on distinct expr result)
|
||||
Debug bool `yaml:"debug"` //Debug, when set to true, will enable debugging for _this_ scenario specifically
|
||||
Labels map[string]interface{} `yaml:"labels"` //Labels is K:V list aiming at providing context the overflow
|
||||
Blackhole string `yaml:"blackhole,omitempty"` //Blackhole is a duration that, if present, will prevent same bucket partition to overflow more often than $duration
|
||||
logger *log.Entry `yaml:"-"` //logger is bucket-specific logger (used by Debug as well)
|
||||
Reprocess bool `yaml:"reprocess"` //Reprocess, if true, will for the bucket to be re-injected into processing chain
|
||||
CacheSize int `yaml:"cache_size"` //CacheSize, if > 0, limits the size of in-memory cache of the bucket
|
||||
Profiling bool `yaml:"profiling"` //Profiling, if true, will make the bucket record pours/overflows/etc.
|
||||
OverflowFilter string `yaml:"overflow_filter"` //OverflowFilter if present, is a filter that must return true for the overflow to go through
|
||||
ConditionalOverflow string `yaml:"condition"` //condition if present, is an expression that must return true for the bucket to overflow
|
||||
BayesianPrior float32 `yaml:"bayesian_prior"`
|
||||
BayesianThreshold float32 `yaml:"bayesian_threshold"`
|
||||
BayesianConditions []RawBayesianCondition `yaml:"bayesian_conditions"` //conditions for the bayesian bucket
|
||||
ScopeType types.ScopeType `yaml:"scope,omitempty"` //to enforce a different remediation than blocking an IP. Will default this to IP
|
||||
BucketName string `yaml:"-"`
|
||||
Filename string `yaml:"-"`
|
||||
RunTimeFilter *vm.Program `json:"-"`
|
||||
RunTimeGroupBy *vm.Program `json:"-"`
|
||||
Data []*types.DataSource `yaml:"data,omitempty"`
|
||||
DataDir string `yaml:"-"`
|
||||
CancelOnFilter string `yaml:"cancel_on,omitempty"` //a filter that, if matched, kills the bucket
|
||||
leakspeed time.Duration //internal representation of `Leakspeed`
|
||||
duration time.Duration //internal representation of `Duration`
|
||||
ret chan types.Event //the bucket-specific output chan for overflows
|
||||
processors []Processor //processors is the list of hooks for pour/overflow/create (cf. uniq, blackhole etc.)
|
||||
output bool //??
|
||||
ScenarioVersion string `yaml:"version,omitempty"`
|
||||
hash string `yaml:"-"`
|
||||
Simulated bool `yaml:"simulated"` //Set to true if the scenario instantiating the bucket was in the exclusion list
|
||||
tomb *tomb.Tomb `yaml:"-"`
|
||||
wgPour *sync.WaitGroup `yaml:"-"`
|
||||
wgDumpState *sync.WaitGroup `yaml:"-"`
|
||||
orderEvent bool
|
||||
}
|
||||
|
||||
|
@ -314,12 +313,6 @@ func LoadBucket(bucketFactory *BucketFactory, tomb *tomb.Tomb) error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("invalid filter '%s' in %s : %v", bucketFactory.Filter, bucketFactory.Filename, err)
|
||||
}
|
||||
if bucketFactory.Debug {
|
||||
bucketFactory.ExprDebugger, err = exprhelpers.NewDebugger(bucketFactory.Filter, exprhelpers.GetExprOptions(map[string]interface{}{"evt": &types.Event{}})...)
|
||||
if err != nil {
|
||||
log.Errorf("unable to build debug filter for '%s' : %s", bucketFactory.Filter, err)
|
||||
}
|
||||
}
|
||||
|
||||
if bucketFactory.GroupBy != "" {
|
||||
bucketFactory.RunTimeGroupBy, err = expr.Compile(bucketFactory.GroupBy, exprhelpers.GetExprOptions(map[string]interface{}{"evt": &types.Event{}})...)
|
||||
|
|
|
@ -9,11 +9,11 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/antonmedv/expr"
|
||||
"github.com/mohae/deepcopy"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
)
|
||||
|
||||
|
@ -297,7 +297,6 @@ func PourItemToHolders(parsed types.Event, holders []BucketFactory, buckets *Buc
|
|||
evt := deepcopy.Copy(parsed)
|
||||
BucketPourCache["OK"] = append(BucketPourCache["OK"], evt.(types.Event))
|
||||
}
|
||||
parserEnv := map[string]interface{}{"evt": &parsed}
|
||||
//find the relevant holders (scenarios)
|
||||
for idx := 0; idx < len(holders); idx++ {
|
||||
//for idx, holder := range holders {
|
||||
|
@ -305,7 +304,10 @@ func PourItemToHolders(parsed types.Event, holders []BucketFactory, buckets *Buc
|
|||
//evaluate bucket's condition
|
||||
if holders[idx].RunTimeFilter != nil {
|
||||
holders[idx].logger.Tracef("event against holder %d/%d", idx, len(holders))
|
||||
output, err := expr.Run(holders[idx].RunTimeFilter, parserEnv)
|
||||
output, err := exprhelpers.Run(holders[idx].RunTimeFilter,
|
||||
map[string]interface{}{"evt": &parsed},
|
||||
holders[idx].logger,
|
||||
holders[idx].Debug)
|
||||
if err != nil {
|
||||
holders[idx].logger.Errorf("failed parsing : %v", err)
|
||||
return false, fmt.Errorf("leaky failed : %s", err)
|
||||
|
@ -315,10 +317,6 @@ func PourItemToHolders(parsed types.Event, holders []BucketFactory, buckets *Buc
|
|||
holders[idx].logger.Errorf("unexpected non-bool return : %T", output)
|
||||
holders[idx].logger.Fatalf("Filter issue")
|
||||
}
|
||||
|
||||
if holders[idx].Debug {
|
||||
holders[idx].ExprDebugger.Run(holders[idx].logger, condition, parserEnv)
|
||||
}
|
||||
if !condition {
|
||||
holders[idx].logger.Debugf("Event leaving node : ko (filter mismatch)")
|
||||
continue
|
||||
|
@ -328,7 +326,7 @@ func PourItemToHolders(parsed types.Event, holders []BucketFactory, buckets *Buc
|
|||
//groupby determines the partition key for the specific bucket
|
||||
var groupby string
|
||||
if holders[idx].RunTimeGroupBy != nil {
|
||||
tmpGroupBy, err := expr.Run(holders[idx].RunTimeGroupBy, parserEnv)
|
||||
tmpGroupBy, err := exprhelpers.Run(holders[idx].RunTimeGroupBy, map[string]interface{}{"evt": &parsed}, holders[idx].logger, holders[idx].Debug)
|
||||
if err != nil {
|
||||
holders[idx].logger.Errorf("failed groupby : %v", err)
|
||||
return false, errors.New("leaky failed :/")
|
||||
|
|
|
@ -28,7 +28,7 @@ func NewOverflowFilter(g *BucketFactory) (*OverflowFilter, error) {
|
|||
u := OverflowFilter{}
|
||||
u.Filter = g.OverflowFilter
|
||||
|
||||
u.FilterRuntime, err = expr.Compile(u.Filter, exprhelpers.GetExprOptions(map[string]interface{}{"queue": &Queue{}, "signal": &types.RuntimeAlert{}, "leaky": &Leaky{}})...)
|
||||
u.FilterRuntime, err = expr.Compile(u.Filter, exprhelpers.GetExprOptions(map[string]interface{}{"queue": &types.Queue{}, "signal": &types.RuntimeAlert{}, "leaky": &Leaky{}})...)
|
||||
if err != nil {
|
||||
g.logger.Errorf("Unable to compile filter : %v", err)
|
||||
return nil, fmt.Errorf("unable to compile filter : %v", err)
|
||||
|
@ -36,10 +36,10 @@ func NewOverflowFilter(g *BucketFactory) (*OverflowFilter, error) {
|
|||
return &u, nil
|
||||
}
|
||||
|
||||
func (u *OverflowFilter) OnBucketOverflow(Bucket *BucketFactory) func(*Leaky, types.RuntimeAlert, *Queue) (types.RuntimeAlert, *Queue) {
|
||||
return func(l *Leaky, s types.RuntimeAlert, q *Queue) (types.RuntimeAlert, *Queue) {
|
||||
el, err := expr.Run(u.FilterRuntime, map[string]interface{}{
|
||||
"queue": q, "signal": s, "leaky": l})
|
||||
func (u *OverflowFilter) OnBucketOverflow(Bucket *BucketFactory) func(*Leaky, types.RuntimeAlert, *types.Queue) (types.RuntimeAlert, *types.Queue) {
|
||||
return func(l *Leaky, s types.RuntimeAlert, q *types.Queue) (types.RuntimeAlert, *types.Queue) {
|
||||
el, err := exprhelpers.Run(u.FilterRuntime, map[string]interface{}{
|
||||
"queue": q, "signal": s, "leaky": l}, l.logger, Bucket.Debug)
|
||||
if err != nil {
|
||||
l.logger.Errorf("Failed running overflow filter: %s", err)
|
||||
return s, q
|
||||
|
|
|
@ -6,12 +6,12 @@ import (
|
|||
"sort"
|
||||
"strconv"
|
||||
|
||||
"github.com/antonmedv/expr"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/go-openapi/strfmt"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/alertcontext"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/models"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
)
|
||||
|
@ -50,7 +50,7 @@ func SourceFromEvent(evt types.Event, leaky *Leaky) (map[string]models.Source, e
|
|||
*src.Value = v.Range
|
||||
}
|
||||
if leaky.scopeType.RunTimeFilter != nil {
|
||||
retValue, err := expr.Run(leaky.scopeType.RunTimeFilter, map[string]interface{}{"evt": &evt})
|
||||
retValue, err := exprhelpers.Run(leaky.scopeType.RunTimeFilter, map[string]interface{}{"evt": &evt}, leaky.logger, leaky.BucketConfig.Debug)
|
||||
if err != nil {
|
||||
return srcs, fmt.Errorf("while running scope filter: %w", err)
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ func SourceFromEvent(evt types.Event, leaky *Leaky) (map[string]models.Source, e
|
|||
} else if leaky.scopeType.Scope == types.Range {
|
||||
src.Value = &src.Range
|
||||
if leaky.scopeType.RunTimeFilter != nil {
|
||||
retValue, err := expr.Run(leaky.scopeType.RunTimeFilter, map[string]interface{}{"evt": &evt})
|
||||
retValue, err := exprhelpers.Run(leaky.scopeType.RunTimeFilter, map[string]interface{}{"evt": &evt}, leaky.logger, leaky.BucketConfig.Debug)
|
||||
if err != nil {
|
||||
return srcs, fmt.Errorf("while running scope filter: %w", err)
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ func SourceFromEvent(evt types.Event, leaky *Leaky) (map[string]models.Source, e
|
|||
if leaky.scopeType.RunTimeFilter == nil {
|
||||
return srcs, fmt.Errorf("empty scope information")
|
||||
}
|
||||
retValue, err := expr.Run(leaky.scopeType.RunTimeFilter, map[string]interface{}{"evt": &evt})
|
||||
retValue, err := exprhelpers.Run(leaky.scopeType.RunTimeFilter, map[string]interface{}{"evt": &evt}, leaky.logger, leaky.BucketConfig.Debug)
|
||||
if err != nil {
|
||||
return srcs, fmt.Errorf("while running scope filter: %w", err)
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ func SourceFromEvent(evt types.Event, leaky *Leaky) (map[string]models.Source, e
|
|||
}
|
||||
|
||||
// EventsFromQueue iterates the queue to collect & prepare meta-datas from alert
|
||||
func EventsFromQueue(queue *Queue) []*models.Event {
|
||||
func EventsFromQueue(queue *types.Queue) []*models.Event {
|
||||
|
||||
events := []*models.Event{}
|
||||
|
||||
|
@ -207,7 +207,7 @@ func EventsFromQueue(queue *Queue) []*models.Event {
|
|||
}
|
||||
|
||||
// alertFormatSource iterates over the queue to collect sources
|
||||
func alertFormatSource(leaky *Leaky, queue *Queue) (map[string]models.Source, string, error) {
|
||||
func alertFormatSource(leaky *Leaky, queue *types.Queue) (map[string]models.Source, string, error) {
|
||||
var sources = make(map[string]models.Source)
|
||||
var source_type string
|
||||
|
||||
|
@ -233,7 +233,7 @@ func alertFormatSource(leaky *Leaky, queue *Queue) (map[string]models.Source, st
|
|||
}
|
||||
|
||||
// NewAlert will generate a RuntimeAlert and its APIAlert(s) from a bucket that overflowed
|
||||
func NewAlert(leaky *Leaky, queue *Queue) (types.RuntimeAlert, error) {
|
||||
func NewAlert(leaky *Leaky, queue *types.Queue) (types.RuntimeAlert, error) {
|
||||
var runtimeAlert types.RuntimeAlert
|
||||
|
||||
leaky.logger.Tracef("Overflow (start: %s, end: %s)", leaky.First_ts, leaky.Ovflw_ts)
|
||||
|
|
|
@ -5,7 +5,7 @@ import "github.com/crowdsecurity/crowdsec/pkg/types"
|
|||
type Processor interface {
|
||||
OnBucketInit(Bucket *BucketFactory) error
|
||||
OnBucketPour(Bucket *BucketFactory) func(types.Event, *Leaky) *types.Event
|
||||
OnBucketOverflow(Bucket *BucketFactory) func(*Leaky, types.RuntimeAlert, *Queue) (types.RuntimeAlert, *Queue)
|
||||
OnBucketOverflow(Bucket *BucketFactory) func(*Leaky, types.RuntimeAlert, *types.Queue) (types.RuntimeAlert, *types.Queue)
|
||||
|
||||
AfterBucketPour(Bucket *BucketFactory) func(types.Event, *Leaky) *types.Event
|
||||
}
|
||||
|
@ -23,8 +23,8 @@ func (d *DumbProcessor) OnBucketPour(bucketFactory *BucketFactory) func(types.Ev
|
|||
}
|
||||
}
|
||||
|
||||
func (d *DumbProcessor) OnBucketOverflow(b *BucketFactory) func(*Leaky, types.RuntimeAlert, *Queue) (types.RuntimeAlert, *Queue) {
|
||||
return func(leaky *Leaky, alert types.RuntimeAlert, queue *Queue) (types.RuntimeAlert, *Queue) {
|
||||
func (d *DumbProcessor) OnBucketOverflow(b *BucketFactory) func(*Leaky, types.RuntimeAlert, *types.Queue) (types.RuntimeAlert, *types.Queue) {
|
||||
return func(leaky *Leaky, alert types.RuntimeAlert, queue *types.Queue) (types.RuntimeAlert, *types.Queue) {
|
||||
return alert, queue
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,14 +19,13 @@ import (
|
|||
// Thus, if the bucket receives a request that matches fetching a static resource (here css), it cancels itself
|
||||
|
||||
type CancelOnFilter struct {
|
||||
CancelOnFilter *vm.Program
|
||||
CancelOnFilterDebug *exprhelpers.ExprDebugger
|
||||
CancelOnFilter *vm.Program
|
||||
Debug bool
|
||||
}
|
||||
|
||||
var cancelExprCacheLock sync.Mutex
|
||||
var cancelExprCache map[string]struct {
|
||||
CancelOnFilter *vm.Program
|
||||
CancelOnFilterDebug *exprhelpers.ExprDebugger
|
||||
CancelOnFilter *vm.Program
|
||||
}
|
||||
|
||||
func (u *CancelOnFilter) OnBucketPour(bucketFactory *BucketFactory) func(types.Event, *Leaky) *types.Event {
|
||||
|
@ -34,15 +33,11 @@ func (u *CancelOnFilter) OnBucketPour(bucketFactory *BucketFactory) func(types.E
|
|||
var condition, ok bool
|
||||
if u.CancelOnFilter != nil {
|
||||
leaky.logger.Tracef("running cancel_on filter")
|
||||
output, err := expr.Run(u.CancelOnFilter, map[string]interface{}{"evt": &msg})
|
||||
output, err := exprhelpers.Run(u.CancelOnFilter, map[string]interface{}{"evt": &msg}, leaky.logger, u.Debug)
|
||||
if err != nil {
|
||||
leaky.logger.Warningf("cancel_on error : %s", err)
|
||||
return &msg
|
||||
}
|
||||
//only run debugger expression if condition is false
|
||||
if u.CancelOnFilterDebug != nil {
|
||||
u.CancelOnFilterDebug.Run(leaky.logger, condition, map[string]interface{}{"evt": &msg})
|
||||
}
|
||||
if condition, ok = output.(bool); !ok {
|
||||
leaky.logger.Warningf("cancel_on, unexpected non-bool return : %T", output)
|
||||
return &msg
|
||||
|
@ -58,8 +53,8 @@ func (u *CancelOnFilter) OnBucketPour(bucketFactory *BucketFactory) func(types.E
|
|||
}
|
||||
}
|
||||
|
||||
func (u *CancelOnFilter) OnBucketOverflow(bucketFactory *BucketFactory) func(*Leaky, types.RuntimeAlert, *Queue) (types.RuntimeAlert, *Queue) {
|
||||
return func(leaky *Leaky, alert types.RuntimeAlert, queue *Queue) (types.RuntimeAlert, *Queue) {
|
||||
func (u *CancelOnFilter) OnBucketOverflow(bucketFactory *BucketFactory) func(*Leaky, types.RuntimeAlert, *types.Queue) (types.RuntimeAlert, *types.Queue) {
|
||||
return func(leaky *Leaky, alert types.RuntimeAlert, queue *types.Queue) (types.RuntimeAlert, *types.Queue) {
|
||||
return alert, queue
|
||||
}
|
||||
}
|
||||
|
@ -73,14 +68,12 @@ func (u *CancelOnFilter) AfterBucketPour(bucketFactory *BucketFactory) func(type
|
|||
func (u *CancelOnFilter) OnBucketInit(bucketFactory *BucketFactory) error {
|
||||
var err error
|
||||
var compiledExpr struct {
|
||||
CancelOnFilter *vm.Program
|
||||
CancelOnFilterDebug *exprhelpers.ExprDebugger
|
||||
CancelOnFilter *vm.Program
|
||||
}
|
||||
|
||||
if cancelExprCache == nil {
|
||||
cancelExprCache = make(map[string]struct {
|
||||
CancelOnFilter *vm.Program
|
||||
CancelOnFilterDebug *exprhelpers.ExprDebugger
|
||||
CancelOnFilter *vm.Program
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -88,7 +81,6 @@ func (u *CancelOnFilter) OnBucketInit(bucketFactory *BucketFactory) error {
|
|||
if compiled, ok := cancelExprCache[bucketFactory.CancelOnFilter]; ok {
|
||||
cancelExprCacheLock.Unlock()
|
||||
u.CancelOnFilter = compiled.CancelOnFilter
|
||||
u.CancelOnFilterDebug = compiled.CancelOnFilterDebug
|
||||
return nil
|
||||
} else {
|
||||
cancelExprCacheLock.Unlock()
|
||||
|
@ -101,13 +93,7 @@ func (u *CancelOnFilter) OnBucketInit(bucketFactory *BucketFactory) error {
|
|||
}
|
||||
u.CancelOnFilter = compiledExpr.CancelOnFilter
|
||||
if bucketFactory.Debug {
|
||||
compiledExpr.CancelOnFilterDebug, err = exprhelpers.NewDebugger(bucketFactory.CancelOnFilter, exprhelpers.GetExprOptions(map[string]interface{}{"evt": &types.Event{}})...,
|
||||
)
|
||||
if err != nil {
|
||||
bucketFactory.logger.Errorf("reset_filter debug error : %s", err)
|
||||
return err
|
||||
}
|
||||
u.CancelOnFilterDebug = compiledExpr.CancelOnFilterDebug
|
||||
u.Debug = true
|
||||
}
|
||||
cancelExprCacheLock.Lock()
|
||||
cancelExprCache[bucketFactory.CancelOnFilter] = compiledExpr
|
||||
|
|
|
@ -47,8 +47,8 @@ func (u *Uniq) OnBucketPour(bucketFactory *BucketFactory) func(types.Event, *Lea
|
|||
}
|
||||
}
|
||||
|
||||
func (u *Uniq) OnBucketOverflow(bucketFactory *BucketFactory) func(*Leaky, types.RuntimeAlert, *Queue) (types.RuntimeAlert, *Queue) {
|
||||
return func(leaky *Leaky, alert types.RuntimeAlert, queue *Queue) (types.RuntimeAlert, *Queue) {
|
||||
func (u *Uniq) OnBucketOverflow(bucketFactory *BucketFactory) func(*Leaky, types.RuntimeAlert, *types.Queue) (types.RuntimeAlert, *types.Queue) {
|
||||
return func(leaky *Leaky, alert types.RuntimeAlert, queue *types.Queue) (types.RuntimeAlert, *types.Queue) {
|
||||
return alert, queue
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,9 +42,8 @@ type Node struct {
|
|||
rn string //this is only for us in debug, a random generated name for each node
|
||||
//Filter is executed at runtime (with current log line as context)
|
||||
//and must succeed or node is exited
|
||||
Filter string `yaml:"filter,omitempty"`
|
||||
RunTimeFilter *vm.Program `yaml:"-" json:"-"` //the actual compiled filter
|
||||
ExprDebugger *exprhelpers.ExprDebugger `yaml:"-" json:"-"` //used to debug expression by printing the content of each variable of the expression
|
||||
Filter string `yaml:"filter,omitempty"`
|
||||
RunTimeFilter *vm.Program `yaml:"-" json:"-"` //the actual compiled filter
|
||||
//If node has leafs, execute all of them until one asks for a 'break'
|
||||
LeavesNodes []Node `yaml:"nodes,omitempty"`
|
||||
//Flag used to describe when to 'break' or return an 'error'
|
||||
|
@ -141,7 +140,7 @@ func (n *Node) process(p *types.Event, ctx UnixParserCtx, expressionEnv map[stri
|
|||
clog.Tracef("Event entering node")
|
||||
if n.RunTimeFilter != nil {
|
||||
//Evaluate node's filter
|
||||
output, err := expr.Run(n.RunTimeFilter, cachedExprEnv)
|
||||
output, err := exprhelpers.Run(n.RunTimeFilter, cachedExprEnv, clog, n.Debug)
|
||||
if err != nil {
|
||||
clog.Warningf("failed to run filter : %v", err)
|
||||
clog.Debugf("Event leaving node : ko")
|
||||
|
@ -150,9 +149,6 @@ func (n *Node) process(p *types.Event, ctx UnixParserCtx, expressionEnv map[stri
|
|||
|
||||
switch out := output.(type) {
|
||||
case bool:
|
||||
if n.Debug {
|
||||
n.ExprDebugger.Run(clog, out, cachedExprEnv)
|
||||
}
|
||||
if !out {
|
||||
clog.Debugf("Event leaving node : ko (failed filter)")
|
||||
return false, nil
|
||||
|
@ -180,7 +176,6 @@ func (n *Node) process(p *types.Event, ctx UnixParserCtx, expressionEnv map[stri
|
|||
// Previous code returned nil if there was an error, so we keep this behavior
|
||||
return false, nil //nolint:nilerr
|
||||
}
|
||||
|
||||
if isWhitelisted && !p.Whitelisted {
|
||||
p.Whitelisted = true
|
||||
p.WhitelistReason = n.Whitelist.Reason
|
||||
|
@ -211,7 +206,7 @@ func (n *Node) process(p *types.Event, ctx UnixParserCtx, expressionEnv map[stri
|
|||
NodeState = false
|
||||
}
|
||||
} else if n.Grok.RunTimeValue != nil {
|
||||
output, err := expr.Run(n.Grok.RunTimeValue, cachedExprEnv)
|
||||
output, err := exprhelpers.Run(n.Grok.RunTimeValue, cachedExprEnv, clog, n.Debug)
|
||||
if err != nil {
|
||||
clog.Warningf("failed to run RunTimeValue : %v", err)
|
||||
NodeState = false
|
||||
|
@ -274,7 +269,7 @@ func (n *Node) process(p *types.Event, ctx UnixParserCtx, expressionEnv map[stri
|
|||
continue
|
||||
}
|
||||
//collect the data
|
||||
output, err := expr.Run(stash.ValueExpression, cachedExprEnv)
|
||||
output, err := exprhelpers.Run(stash.ValueExpression, cachedExprEnv, clog, n.Debug)
|
||||
if err != nil {
|
||||
clog.Warningf("Error while running stash val expression : %v", err)
|
||||
}
|
||||
|
@ -288,7 +283,7 @@ func (n *Node) process(p *types.Event, ctx UnixParserCtx, expressionEnv map[stri
|
|||
}
|
||||
|
||||
//collect the key
|
||||
output, err = expr.Run(stash.KeyExpression, cachedExprEnv)
|
||||
output, err = exprhelpers.Run(stash.KeyExpression, cachedExprEnv, clog, n.Debug)
|
||||
if err != nil {
|
||||
clog.Warningf("Error while running stash key expression : %v", err)
|
||||
}
|
||||
|
@ -425,14 +420,6 @@ func (n *Node) compile(pctx *UnixParserCtx, ectx EnricherCtx) error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("compilation of '%s' failed: %v", n.Filter, err)
|
||||
}
|
||||
|
||||
if n.Debug {
|
||||
n.ExprDebugger, err = exprhelpers.NewDebugger(n.Filter, exprhelpers.GetExprOptions(map[string]interface{}{"evt": &types.Event{}})...)
|
||||
if err != nil {
|
||||
log.Errorf("unable to build debug filter for '%s' : %s", n.Filter, err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* handle pattern_syntax and groks */
|
||||
|
|
|
@ -14,11 +14,11 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/antonmedv/expr"
|
||||
"github.com/mohae/deepcopy"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
)
|
||||
|
||||
|
@ -117,7 +117,7 @@ func (n *Node) ProcessStatics(statics []ExtraField, event *types.Event) error {
|
|||
if static.Value != "" {
|
||||
value = static.Value
|
||||
} else if static.RunTimeValue != nil {
|
||||
output, err := expr.Run(static.RunTimeValue, map[string]interface{}{"evt": event})
|
||||
output, err := exprhelpers.Run(static.RunTimeValue, map[string]interface{}{"evt": event}, clog, n.Debug)
|
||||
if err != nil {
|
||||
clog.Warningf("failed to run RunTimeValue : %v", err)
|
||||
continue
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
|
||||
"github.com/antonmedv/expr"
|
||||
"github.com/antonmedv/expr/vm"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
)
|
||||
|
@ -22,8 +21,7 @@ type Whitelist struct {
|
|||
}
|
||||
|
||||
type ExprWhitelist struct {
|
||||
Filter *vm.Program
|
||||
ExprDebugger *exprhelpers.ExprDebugger // used to debug expression by printing the content of each variable of the expression
|
||||
Filter *vm.Program
|
||||
}
|
||||
|
||||
func (n *Node) ContainsWLs() bool {
|
||||
|
@ -79,7 +77,8 @@ func (n *Node) CheckExprWL(cachedExprEnv map[string]interface{}) (bool, error) {
|
|||
if isWhitelisted {
|
||||
break
|
||||
}
|
||||
output, err := expr.Run(e.Filter, cachedExprEnv)
|
||||
|
||||
output, err := exprhelpers.Run(e.Filter, cachedExprEnv, n.Logger, n.Debug)
|
||||
if err != nil {
|
||||
n.Logger.Warningf("failed to run whitelist expr : %v", err)
|
||||
n.Logger.Debug("Event leaving node : ko")
|
||||
|
@ -87,9 +86,6 @@ func (n *Node) CheckExprWL(cachedExprEnv map[string]interface{}) (bool, error) {
|
|||
}
|
||||
switch out := output.(type) {
|
||||
case bool:
|
||||
if n.Debug {
|
||||
e.ExprDebugger.Run(n.Logger, out, cachedExprEnv)
|
||||
}
|
||||
if out {
|
||||
n.Logger.Debugf("Event is whitelisted by expr, reason [%s]", n.Whitelist.Reason)
|
||||
isWhitelisted = true
|
||||
|
@ -123,11 +119,6 @@ func (n *Node) CompileWLs() (bool, error) {
|
|||
if err != nil {
|
||||
return false, fmt.Errorf("unable to compile whitelist expression '%s' : %v", filter, err)
|
||||
}
|
||||
expression.ExprDebugger, err = exprhelpers.NewDebugger(filter, exprhelpers.GetExprOptions(map[string]interface{}{"evt": &types.Event{}})...)
|
||||
if err != nil {
|
||||
n.Logger.Errorf("unable to build debug filter for '%s' : %s", filter, err)
|
||||
}
|
||||
|
||||
n.Whitelist.B_Exprs = append(n.Whitelist.B_Exprs, expression)
|
||||
n.Logger.Debugf("adding expression %s to whitelists", filter)
|
||||
}
|
||||
|
|
|
@ -983,6 +983,16 @@ func TestDetectDatasourceValidation(t *testing.T) {
|
|||
source: kafka`,
|
||||
expected: setup.Setup{Setup: []setup.ServiceSetup{}},
|
||||
expectedErr: "invalid datasource for foobar: cannot create a kafka reader with an empty list of broker addresses",
|
||||
}, {
|
||||
name: "source loki: required fields",
|
||||
config: `
|
||||
version: 1.0
|
||||
detect:
|
||||
foobar:
|
||||
datasource:
|
||||
source: loki`,
|
||||
expected: setup.Setup{Setup: []setup.ServiceSetup{}},
|
||||
expectedErr: "invalid datasource for foobar: loki query is mandatory",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
package leakybucket
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Queue holds a limited size queue
|
||||
type Queue struct {
|
||||
Queue []types.Event
|
||||
Queue []Event
|
||||
L int //capacity
|
||||
}
|
||||
|
||||
|
@ -15,12 +14,12 @@ type Queue struct {
|
|||
func NewQueue(l int) *Queue {
|
||||
if l == -1 {
|
||||
return &Queue{
|
||||
Queue: make([]types.Event, 0),
|
||||
Queue: make([]Event, 0),
|
||||
L: int(^uint(0) >> 1), // max integer value, architecture independent
|
||||
}
|
||||
}
|
||||
q := &Queue{
|
||||
Queue: make([]types.Event, 0, l),
|
||||
Queue: make([]Event, 0, l),
|
||||
L: l,
|
||||
}
|
||||
log.WithFields(log.Fields{"Capacity": q.L}).Debugf("Creating queue")
|
||||
|
@ -29,7 +28,7 @@ func NewQueue(l int) *Queue {
|
|||
|
||||
// Add an event in the queue. If it has already l elements, the first
|
||||
// element is dropped before adding the new m element
|
||||
func (q *Queue) Add(m types.Event) {
|
||||
func (q *Queue) Add(m Event) {
|
||||
for len(q.Queue) > q.L { //we allow to add one element more than the true capacity
|
||||
q.Queue = q.Queue[1:]
|
||||
}
|
||||
|
@ -37,6 +36,6 @@ func (q *Queue) Add(m types.Event) {
|
|||
}
|
||||
|
||||
// GetQueue returns the entire queue
|
||||
func (q *Queue) GetQueue() []types.Event {
|
||||
func (q *Queue) GetQueue() []Event {
|
||||
return q.Queue
|
||||
}
|
|
@ -46,7 +46,7 @@ func SetDefaultLoggerConfig(cfgMode string, cfgFolder string, cfgLevel log.Level
|
|||
}
|
||||
logLevel = cfgLevel
|
||||
log.SetLevel(logLevel)
|
||||
logFormatter = &log.TextFormatter{TimestampFormat: "02-01-2006 15:04:05", FullTimestamp: true, ForceColors: forceColors}
|
||||
logFormatter = &log.TextFormatter{TimestampFormat: "2006-01-02 15:04:05", FullTimestamp: true, ForceColors: forceColors}
|
||||
log.SetFormatter(logFormatter)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# vim: set ft=yaml.ansible:
|
||||
---
|
||||
|
||||
golang_version: "1.21.3"
|
||||
golang_version: "1.21.4"
|
||||
golang_install_dir: "/opt/go/{{ golang_version }}"
|
||||
|
|
|
@ -110,6 +110,37 @@ teardown() {
|
|||
assert_json '["http://127.0.0.1:8080/","githubciXXXXXXXXXXXXXXXXXXXXXXXX"]'
|
||||
}
|
||||
|
||||
@test "cscli - required configuration paths" {
|
||||
config=$(cat "${CONFIG_YAML}")
|
||||
configdir=$(config_get '.config_paths.config_dir')
|
||||
|
||||
# required configuration paths with no defaults
|
||||
|
||||
config_set 'del(.config_paths)'
|
||||
rune -1 cscli hub list
|
||||
assert_stderr --partial 'no configuration paths provided'
|
||||
echo "$config" > "${CONFIG_YAML}"
|
||||
|
||||
config_set 'del(.config_paths.data_dir)'
|
||||
rune -1 cscli hub list
|
||||
assert_stderr --partial "please provide a data directory with the 'data_dir' directive in the 'config_paths' section"
|
||||
echo "$config" > "${CONFIG_YAML}"
|
||||
|
||||
# defaults
|
||||
|
||||
config_set 'del(.config_paths.hub_dir)'
|
||||
rune -0 cscli hub list
|
||||
rune -0 cscli config show --key Config.ConfigPaths.HubDir
|
||||
assert_output "$configdir/hub"
|
||||
echo "$config" > "${CONFIG_YAML}"
|
||||
|
||||
config_set 'del(.config_paths.index_path)'
|
||||
rune -0 cscli hub list
|
||||
rune -0 cscli config show --key Config.ConfigPaths.HubIndexFile
|
||||
assert_output "$configdir/hub/.index.json"
|
||||
echo "$config" > "${CONFIG_YAML}"
|
||||
}
|
||||
|
||||
@test "cscli config show-yaml" {
|
||||
rune -0 cscli config show-yaml
|
||||
rune -0 yq .common.log_level <(output)
|
||||
|
@ -245,35 +276,6 @@ teardown() {
|
|||
assert_output --partial "# bash completion for cscli"
|
||||
}
|
||||
|
||||
@test "cscli hub list" {
|
||||
# we check for the presence of some objects. There may be others when we
|
||||
# use $PACKAGE_TESTING, so the order is not important.
|
||||
|
||||
rune -0 cscli parsers install crowdsecurity/whitelists
|
||||
rune -0 cscli scenarios install crowdsecurity/asterisk_user_enum
|
||||
rune -0 cscli collections install crowdsecurity/sshd
|
||||
rune -0 cscli postoverflows install crowdsecurity/rdns
|
||||
|
||||
rune -0 cscli hub list -o human
|
||||
assert_line --regexp '^ crowdsecurity/whitelists'
|
||||
assert_line --regexp '^ crowdsecurity/asterisk_user_enum'
|
||||
assert_line --regexp '^ crowdsecurity/sshd'
|
||||
assert_line --regexp '^ crowdsecurity/rdns'
|
||||
|
||||
rune -0 cscli hub list -o raw
|
||||
assert_line --regexp '^crowdsecurity/whitelists,enabled,.*'
|
||||
assert_line --regexp '^crowdsecurity/asterisk_user_enum,enabled,.*'
|
||||
assert_line --regexp '^crowdsecurity/sshd,enabled,.*'
|
||||
assert_line --regexp '^crowdsecurity/rdns,enabled,.*'
|
||||
|
||||
rune -0 cscli hub list -o json
|
||||
rune -0 jq -r '.collections[].name, .parsers[].name, .scenarios[].name, .postoverflows[].name' <(output)
|
||||
assert_line 'crowdsecurity/whitelists'
|
||||
assert_line 'crowdsecurity/asterisk_user_enum'
|
||||
assert_line 'crowdsecurity/sshd'
|
||||
assert_line 'crowdsecurity/rdns'
|
||||
}
|
||||
|
||||
@test "cscli support dump (smoke test)" {
|
||||
rune -0 cscli support dump -f "$BATS_TEST_TMPDIR"/dump.zip
|
||||
assert_file_exists "$BATS_TEST_TMPDIR"/dump.zip
|
||||
|
|
|
@ -36,6 +36,18 @@ teardown() {
|
|||
assert_output '[]'
|
||||
}
|
||||
|
||||
@test "we can create a bouncer with a known key" {
|
||||
# also test the output formats since we know the key
|
||||
rune -0 cscli bouncers add ciTestBouncer --key "foobarbaz" -o human
|
||||
assert_output --partial 'foobarbaz'
|
||||
rune -0 cscli bouncers delete ciTestBouncer
|
||||
rune -0 cscli bouncers add ciTestBouncer --key "foobarbaz" -o json
|
||||
assert_output '"foobarbaz"'
|
||||
rune -0 cscli bouncers delete ciTestBouncer
|
||||
rune -0 cscli bouncers add ciTestBouncer --key "foobarbaz" -o raw
|
||||
assert_output foobarbaz
|
||||
}
|
||||
|
||||
@test "we can't add the same bouncer twice" {
|
||||
rune -0 cscli bouncers add ciTestBouncer
|
||||
rune -1 cscli bouncers add ciTestBouncer -o json
|
||||
|
|
|
@ -71,6 +71,24 @@ teardown() {
|
|||
assert_stderr --partial "can't find crowdsecurity/mysql-bf in scenarios, required by crowdsecurity/mysql"
|
||||
}
|
||||
|
||||
@test "loading hub reports tainted items (subitem is tainted)" {
|
||||
rune -0 cscli collections install crowdsecurity/sshd
|
||||
rune -0 cscli hub list
|
||||
refute_stderr --partial "tainted"
|
||||
rune -0 truncate -s0 "$CONFIG_DIR/parsers/s01-parse/sshd-logs.yaml"
|
||||
rune -0 cscli hub list
|
||||
assert_stderr --partial "crowdsecurity/sshd is tainted because parsers:crowdsecurity/sshd-logs is tainted"
|
||||
}
|
||||
|
||||
@test "loading hub reports tainted items (subitem is not installed)" {
|
||||
rune -0 cscli collections install crowdsecurity/sshd
|
||||
rune -0 cscli hub list
|
||||
refute_stderr --partial "tainted"
|
||||
rune -0 rm "$CONFIG_DIR/parsers/s01-parse/sshd-logs.yaml"
|
||||
rune -0 cscli hub list
|
||||
assert_stderr --partial "crowdsecurity/sshd is tainted because parsers:crowdsecurity/sshd-logs is missing"
|
||||
}
|
||||
|
||||
@test "cscli hub update" {
|
||||
rm -f "$INDEX_PATH"
|
||||
rune -0 cscli hub update
|
||||
|
|
|
@ -109,6 +109,8 @@ teardown() {
|
|||
# removing linux should remove syslog-logs even though sshd depends on it
|
||||
rune -0 cscli collections remove crowdsecurity/linux
|
||||
refute_stderr --partial "crowdsecurity/syslog-logs was not removed"
|
||||
# we must also consider indirect dependencies
|
||||
refute_stderr --partial "crowdsecurity/ssh-bf was not removed"
|
||||
rune -0 cscli parsers list -o json
|
||||
rune -0 jq -e '.parsers | length == 0' <(output)
|
||||
}
|
||||
|
|
|
@ -113,12 +113,12 @@ teardown() {
|
|||
rune -0 mkdir -p "$CONFIG_DIR/collections"
|
||||
rune -0 touch "$CONFIG_DIR/collections/foobar.yaml"
|
||||
rune -0 cscli collections inspect foobar.yaml -o json
|
||||
rune -0 jq -e '.tainted==false' <(output)
|
||||
rune -0 jq -e '[.tainted,.local==false,true]' <(output)
|
||||
|
||||
rune -0 cscli collections install crowdsecurity/sshd
|
||||
rune -0 truncate -s0 "$CONFIG_DIR/collections/sshd.yaml"
|
||||
rune -0 cscli collections inspect crowdsecurity/sshd -o json
|
||||
rune -0 jq -e '.tainted==true' <(output)
|
||||
rune -0 jq -e '[.tainted,.local==true,false]' <(output)
|
||||
|
||||
# and not from hub update
|
||||
rune -0 cscli hub update
|
||||
|
@ -134,7 +134,7 @@ teardown() {
|
|||
assert_output "foobar.yaml"
|
||||
rune -0 cscli collections list foobar.yaml
|
||||
rune -0 cscli collections inspect foobar.yaml -o json
|
||||
rune -0 jq -e '.installed==true' <(output)
|
||||
rune -0 jq -e '[.installed,.local==true,true]' <(output)
|
||||
}
|
||||
|
||||
@test "a local item can provide its own name" {
|
||||
|
@ -145,5 +145,5 @@ teardown() {
|
|||
assert_output "hi-its-me"
|
||||
rune -0 cscli collections list hi-its-me
|
||||
rune -0 cscli collections inspect hi-its-me -o json
|
||||
rune -0 jq -e '.installed==true' <(output)
|
||||
rune -0 jq -e '[.installed,.local]==[true,true]' <(output)
|
||||
}
|
||||
|
|
|
@ -77,3 +77,8 @@ services:
|
|||
interval: 10s
|
||||
retries: 5
|
||||
timeout: 10s
|
||||
|
||||
loki:
|
||||
image: grafana/loki:2.8.0
|
||||
ports:
|
||||
- "3100:3100"
|
||||
|
|
Loading…
Add table
Reference in a new issue