2023-01-19 07:45:50 +00:00
|
|
|
package exprhelpers
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
2023-12-18 08:35:28 +00:00
|
|
|
"errors"
|
2023-01-19 07:45:50 +00:00
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
2024-01-05 14:26:13 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
2023-06-01 14:31:56 +00:00
|
|
|
|
2023-07-28 14:35:08 +00:00
|
|
|
"github.com/crowdsecurity/go-cs-lib/ptr"
|
2023-06-01 14:31:56 +00:00
|
|
|
|
|
|
|
"github.com/crowdsecurity/crowdsec/pkg/cticlient"
|
2023-01-19 07:45:50 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var sampledata = map[string]cticlient.SmokeItem{
|
|
|
|
//1.2.3.4 is a known false positive
|
|
|
|
"1.2.3.4": {
|
|
|
|
Ip: "1.2.3.4",
|
|
|
|
Classifications: cticlient.CTIClassifications{
|
|
|
|
FalsePositives: []cticlient.CTIClassification{
|
|
|
|
{
|
|
|
|
Name: "example_false_positive",
|
|
|
|
Label: "Example False Positive",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
//1.2.3.5 is a known bad-guy, and part of FIRE
|
|
|
|
"1.2.3.5": {
|
|
|
|
Ip: "1.2.3.5",
|
|
|
|
Classifications: cticlient.CTIClassifications{
|
|
|
|
Classifications: []cticlient.CTIClassification{
|
|
|
|
{
|
|
|
|
Name: "community-blocklist",
|
|
|
|
Label: "CrowdSec Community Blocklist",
|
|
|
|
Description: "IP belong to the CrowdSec Community Blocklist",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
//1.2.3.6 is a bad guy (high bg noise), but not in FIRE
|
|
|
|
"1.2.3.6": {
|
|
|
|
Ip: "1.2.3.6",
|
|
|
|
BackgroundNoiseScore: new(int),
|
|
|
|
Behaviors: []*cticlient.CTIBehavior{
|
|
|
|
{Name: "ssh:bruteforce", Label: "SSH Bruteforce", Description: "SSH Bruteforce"},
|
|
|
|
},
|
|
|
|
AttackDetails: []*cticlient.CTIAttackDetails{
|
|
|
|
{Name: "crowdsecurity/ssh-bf", Label: "Example Attack"},
|
|
|
|
{Name: "crowdsecurity/ssh-slow-bf", Label: "Example Attack"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
//1.2.3.7 is a ok guy, but part of a bad range
|
2023-03-09 10:56:02 +00:00
|
|
|
"1.2.3.7": {},
|
2023-01-19 07:45:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const validApiKey = "my-api-key"
|
|
|
|
|
|
|
|
type RoundTripFunc func(req *http.Request) *http.Response
|
|
|
|
|
|
|
|
func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
|
|
return f(req), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func smokeHandler(req *http.Request) *http.Response {
|
|
|
|
apiKey := req.Header.Get("x-api-key")
|
|
|
|
if apiKey != validApiKey {
|
|
|
|
return &http.Response{
|
|
|
|
StatusCode: http.StatusForbidden,
|
|
|
|
Body: nil,
|
|
|
|
Header: make(http.Header),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
requestedIP := strings.Split(req.URL.Path, "/")[3]
|
2024-01-05 14:26:13 +00:00
|
|
|
|
2023-01-19 07:45:50 +00:00
|
|
|
sample, ok := sampledata[requestedIP]
|
|
|
|
if !ok {
|
|
|
|
return &http.Response{
|
|
|
|
StatusCode: http.StatusNotFound,
|
|
|
|
Body: nil,
|
|
|
|
Header: make(http.Header),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
body, err := json.Marshal(sample)
|
|
|
|
if err != nil {
|
|
|
|
return &http.Response{
|
|
|
|
StatusCode: http.StatusInternalServerError,
|
|
|
|
Body: nil,
|
|
|
|
Header: make(http.Header),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
reader := io.NopCloser(bytes.NewReader(body))
|
|
|
|
|
|
|
|
return &http.Response{
|
|
|
|
StatusCode: http.StatusOK,
|
|
|
|
// Send response to be tested
|
|
|
|
Body: reader,
|
|
|
|
Header: make(http.Header),
|
|
|
|
ContentLength: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-17 08:34:53 +00:00
|
|
|
func TestNillClient(t *testing.T) {
|
|
|
|
defer ShutdownCrowdsecCTI()
|
2024-01-05 14:26:13 +00:00
|
|
|
|
2023-12-18 08:35:28 +00:00
|
|
|
if err := InitCrowdsecCTI(ptr.Of(""), nil, nil, nil); !errors.Is(err, cticlient.ErrDisabled) {
|
2023-10-17 08:34:53 +00:00
|
|
|
t.Fatalf("failed to init CTI : %s", err)
|
|
|
|
}
|
2024-01-05 14:26:13 +00:00
|
|
|
|
2023-10-17 08:34:53 +00:00
|
|
|
item, err := CrowdsecCTI("1.2.3.4")
|
|
|
|
assert.Equal(t, err, cticlient.ErrDisabled)
|
|
|
|
assert.Equal(t, item, &cticlient.SmokeItem{})
|
|
|
|
}
|
|
|
|
|
2023-01-19 07:45:50 +00:00
|
|
|
func TestInvalidAuth(t *testing.T) {
|
|
|
|
defer ShutdownCrowdsecCTI()
|
2024-01-05 14:26:13 +00:00
|
|
|
|
2023-06-01 14:31:56 +00:00
|
|
|
if err := InitCrowdsecCTI(ptr.Of("asdasd"), nil, nil, nil); err != nil {
|
2023-01-19 07:45:50 +00:00
|
|
|
t.Fatalf("failed to init CTI : %s", err)
|
|
|
|
}
|
|
|
|
//Replace the client created by InitCrowdsecCTI with one that uses a custom transport
|
|
|
|
ctiClient = cticlient.NewCrowdsecCTIClient(cticlient.WithAPIKey("asdasd"), cticlient.WithHTTPClient(&http.Client{
|
|
|
|
Transport: RoundTripFunc(smokeHandler),
|
|
|
|
}))
|
|
|
|
|
|
|
|
item, err := CrowdsecCTI("1.2.3.4")
|
|
|
|
assert.Equal(t, item, &cticlient.SmokeItem{})
|
2024-01-05 14:26:13 +00:00
|
|
|
assert.False(t, CTIApiEnabled)
|
2023-01-19 07:45:50 +00:00
|
|
|
assert.Equal(t, err, cticlient.ErrUnauthorized)
|
|
|
|
|
|
|
|
//CTI is now disabled, all requests should return empty
|
|
|
|
ctiClient = cticlient.NewCrowdsecCTIClient(cticlient.WithAPIKey(validApiKey), cticlient.WithHTTPClient(&http.Client{
|
|
|
|
Transport: RoundTripFunc(smokeHandler),
|
|
|
|
}))
|
|
|
|
|
|
|
|
item, err = CrowdsecCTI("1.2.3.4")
|
|
|
|
assert.Equal(t, item, &cticlient.SmokeItem{})
|
2024-01-05 14:26:13 +00:00
|
|
|
assert.False(t, CTIApiEnabled)
|
2023-01-19 07:45:50 +00:00
|
|
|
assert.Equal(t, err, cticlient.ErrDisabled)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestNoKey(t *testing.T) {
|
|
|
|
defer ShutdownCrowdsecCTI()
|
2024-01-05 14:26:13 +00:00
|
|
|
|
2023-01-19 07:45:50 +00:00
|
|
|
err := InitCrowdsecCTI(nil, nil, nil, nil)
|
2024-01-05 14:26:13 +00:00
|
|
|
require.ErrorIs(t, err, cticlient.ErrDisabled)
|
2023-01-19 07:45:50 +00:00
|
|
|
//Replace the client created by InitCrowdsecCTI with one that uses a custom transport
|
|
|
|
ctiClient = cticlient.NewCrowdsecCTIClient(cticlient.WithAPIKey("asdasd"), cticlient.WithHTTPClient(&http.Client{
|
|
|
|
Transport: RoundTripFunc(smokeHandler),
|
|
|
|
}))
|
|
|
|
|
|
|
|
item, err := CrowdsecCTI("1.2.3.4")
|
|
|
|
assert.Equal(t, item, &cticlient.SmokeItem{})
|
2024-01-05 14:26:13 +00:00
|
|
|
assert.False(t, CTIApiEnabled)
|
2023-01-19 07:45:50 +00:00
|
|
|
assert.Equal(t, err, cticlient.ErrDisabled)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCache(t *testing.T) {
|
|
|
|
defer ShutdownCrowdsecCTI()
|
2024-01-05 14:26:13 +00:00
|
|
|
|
2023-01-19 07:45:50 +00:00
|
|
|
cacheDuration := 1 * time.Second
|
2023-06-01 14:31:56 +00:00
|
|
|
if err := InitCrowdsecCTI(ptr.Of(validApiKey), &cacheDuration, nil, nil); err != nil {
|
2023-01-19 07:45:50 +00:00
|
|
|
t.Fatalf("failed to init CTI : %s", err)
|
|
|
|
}
|
|
|
|
//Replace the client created by InitCrowdsecCTI with one that uses a custom transport
|
|
|
|
ctiClient = cticlient.NewCrowdsecCTIClient(cticlient.WithAPIKey(validApiKey), cticlient.WithHTTPClient(&http.Client{
|
|
|
|
Transport: RoundTripFunc(smokeHandler),
|
|
|
|
}))
|
|
|
|
|
|
|
|
item, err := CrowdsecCTI("1.2.3.4")
|
2023-03-28 08:49:01 +00:00
|
|
|
ctiResp := item.(*cticlient.SmokeItem)
|
|
|
|
assert.Equal(t, "1.2.3.4", ctiResp.Ip)
|
2024-01-05 14:26:13 +00:00
|
|
|
assert.True(t, CTIApiEnabled)
|
|
|
|
assert.Equal(t, 1, CTICache.Len(true))
|
|
|
|
require.NoError(t, err)
|
2023-01-19 07:45:50 +00:00
|
|
|
|
|
|
|
item, err = CrowdsecCTI("1.2.3.4")
|
2023-03-28 08:49:01 +00:00
|
|
|
ctiResp = item.(*cticlient.SmokeItem)
|
|
|
|
|
|
|
|
assert.Equal(t, "1.2.3.4", ctiResp.Ip)
|
2024-01-05 14:26:13 +00:00
|
|
|
assert.True(t, CTIApiEnabled)
|
|
|
|
assert.Equal(t, 1, CTICache.Len(true))
|
|
|
|
require.NoError(t, err)
|
2023-01-19 07:45:50 +00:00
|
|
|
|
|
|
|
time.Sleep(2 * time.Second)
|
|
|
|
|
2024-01-05 14:26:13 +00:00
|
|
|
assert.Equal(t, 0, CTICache.Len(true))
|
2023-01-19 07:45:50 +00:00
|
|
|
|
|
|
|
item, err = CrowdsecCTI("1.2.3.4")
|
2023-03-28 08:49:01 +00:00
|
|
|
ctiResp = item.(*cticlient.SmokeItem)
|
|
|
|
|
|
|
|
assert.Equal(t, "1.2.3.4", ctiResp.Ip)
|
2024-01-05 14:26:13 +00:00
|
|
|
assert.True(t, CTIApiEnabled)
|
|
|
|
assert.Equal(t, 1, CTICache.Len(true))
|
|
|
|
require.NoError(t, err)
|
2023-01-19 07:45:50 +00:00
|
|
|
}
|