mirror of
https://github.com/crowdsecurity/crowdsec.git
synced 2025-05-10 20:05:55 +02:00
342 lines
8.1 KiB
Go
342 lines
8.1 KiB
Go
package exprhelpers
|
|
|
|
import (
|
|
"net/http"
|
|
"net/url"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestParseQuery(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
query string
|
|
expected url.Values
|
|
}{
|
|
{
|
|
name: "Full URI",
|
|
query: "/foobar/toto?ab=cd&ef=gh",
|
|
expected: url.Values{
|
|
"/foobar/toto?ab": []string{"cd"},
|
|
"ef": []string{"gh"},
|
|
},
|
|
},
|
|
{
|
|
name: "Simple query",
|
|
query: "foo=bar",
|
|
expected: url.Values{
|
|
"foo": []string{"bar"},
|
|
},
|
|
},
|
|
{
|
|
name: "Multiple values",
|
|
query: "foo=bar&foo=baz",
|
|
expected: url.Values{
|
|
"foo": []string{"bar", "baz"},
|
|
},
|
|
},
|
|
{
|
|
name: "Empty value",
|
|
query: "foo=",
|
|
expected: url.Values{
|
|
"foo": []string{""},
|
|
},
|
|
},
|
|
{
|
|
name: "Empty key",
|
|
query: "=bar",
|
|
expected: url.Values{
|
|
"": []string{"bar"},
|
|
},
|
|
},
|
|
{
|
|
name: "Empty query",
|
|
query: "",
|
|
expected: url.Values{},
|
|
},
|
|
{
|
|
name: "Multiple keys",
|
|
query: "foo=bar&baz=qux",
|
|
expected: url.Values{
|
|
"foo": []string{"bar"},
|
|
"baz": []string{"qux"},
|
|
},
|
|
},
|
|
{
|
|
name: "Multiple keys with empty value",
|
|
query: "foo=bar&baz=qux&quux=",
|
|
expected: url.Values{
|
|
"foo": []string{"bar"},
|
|
"baz": []string{"qux"},
|
|
"quux": []string{""},
|
|
},
|
|
},
|
|
{
|
|
name: "Multiple keys with empty value and empty key",
|
|
query: "foo=bar&baz=qux&quux=&=quuz",
|
|
expected: url.Values{
|
|
"foo": []string{"bar"},
|
|
"baz": []string{"qux"},
|
|
"quux": []string{""},
|
|
"": []string{"quuz"},
|
|
},
|
|
},
|
|
{
|
|
name: "Multiple keys with empty value and empty key and multiple values",
|
|
query: "foo=bar&baz=qux&quux=&=quuz&foo=baz",
|
|
expected: url.Values{
|
|
"foo": []string{"bar", "baz"},
|
|
"baz": []string{"qux"},
|
|
"quux": []string{""},
|
|
"": []string{"quuz"},
|
|
},
|
|
},
|
|
{
|
|
name: "Multiple keys with empty value and empty key and multiple values and escaped characters",
|
|
query: "foo=bar&baz=qux&quux=&=quuz&foo=baz&foo=bar%20baz",
|
|
expected: url.Values{
|
|
"foo": []string{"bar", "baz", "bar baz"},
|
|
"baz": []string{"qux"},
|
|
"quux": []string{""},
|
|
"": []string{"quuz"},
|
|
},
|
|
},
|
|
{
|
|
name: "Multiple keys with empty value and empty key and multiple values and escaped characters and semicolon",
|
|
query: "foo=bar&baz=qux&quux=&=quuz&foo=baz&foo=bar%20baz&foo=bar%3Bbaz",
|
|
expected: url.Values{
|
|
"foo": []string{"bar", "baz", "bar baz", "bar;baz"},
|
|
"baz": []string{"qux"},
|
|
"quux": []string{""},
|
|
"": []string{"quuz"},
|
|
},
|
|
},
|
|
{
|
|
name: "Multiple keys with empty value and empty key and multiple values and escaped characters and semicolon and ampersand",
|
|
query: "foo=bar&baz=qux&quux=&=quuz&foo=baz&foo=bar%20baz&foo=bar%3Bbaz&foo=bar%26baz",
|
|
expected: url.Values{
|
|
"foo": []string{"bar", "baz", "bar baz", "bar;baz", "bar&baz"},
|
|
"baz": []string{"qux"},
|
|
"quux": []string{""},
|
|
"": []string{"quuz"},
|
|
},
|
|
},
|
|
{
|
|
name: "Multiple keys with empty value and empty key and multiple values and escaped characters and semicolon and ampersand and equals",
|
|
query: "foo=bar&baz=qux&quux=&=quuz&foo=baz&foo=bar%20baz&foo=bar%3Bbaz&foo=bar%26baz&foo=bar%3Dbaz",
|
|
expected: url.Values{
|
|
"foo": []string{"bar", "baz", "bar baz", "bar;baz", "bar&baz", "bar=baz"},
|
|
"baz": []string{"qux"},
|
|
"quux": []string{""},
|
|
"": []string{"quuz"},
|
|
},
|
|
},
|
|
{
|
|
name: "Multiple keys with empty value and empty key and multiple values and escaped characters and semicolon and ampersand and equals and question mark",
|
|
query: "foo=bar&baz=qux&quux=&=quuz&foo=baz&foo=bar%20baz&foo=bar%3Bbaz&foo=bar%26baz&foo=bar%3Dbaz&foo=bar%3Fbaz",
|
|
expected: url.Values{
|
|
"foo": []string{"bar", "baz", "bar baz", "bar;baz", "bar&baz", "bar=baz", "bar?baz"},
|
|
"baz": []string{"qux"},
|
|
"quux": []string{""},
|
|
"": []string{"quuz"},
|
|
},
|
|
},
|
|
{
|
|
name: "keys with escaped characters",
|
|
query: "foo=ba;r&baz=qu;;x&quux=x\\&ww&xx=qu?uz&",
|
|
expected: url.Values{
|
|
"foo": []string{"ba;r"},
|
|
"baz": []string{"qu;;x"},
|
|
"quux": []string{"x\\"},
|
|
"ww": []string{""},
|
|
"xx": []string{"qu?uz"},
|
|
},
|
|
},
|
|
{
|
|
name: "hexadecimal characters",
|
|
query: "foo=bar%20baz",
|
|
expected: url.Values{
|
|
"foo": []string{"bar baz"},
|
|
},
|
|
},
|
|
{
|
|
name: "hexadecimal characters upper and lower case",
|
|
query: "foo=Ba%42%42&bar=w%2f%2F",
|
|
expected: url.Values{
|
|
"foo": []string{"BaBB"},
|
|
"bar": []string{"w//"},
|
|
},
|
|
},
|
|
{
|
|
name: "hexadecimal characters with invalid characters",
|
|
query: "foo=bar%20baz%2",
|
|
expected: url.Values{
|
|
"foo": []string{"bar baz%2"},
|
|
},
|
|
},
|
|
{
|
|
name: "hexadecimal characters with invalid hex characters",
|
|
query: "foo=bar%xx",
|
|
expected: url.Values{
|
|
"foo": []string{"bar%xx"},
|
|
},
|
|
},
|
|
{
|
|
name: "hexadecimal characters with invalid 2nd hex character",
|
|
query: "foo=bar%2x",
|
|
expected: url.Values{
|
|
"foo": []string{"bar%2x"},
|
|
},
|
|
},
|
|
{
|
|
name: "url +",
|
|
query: "foo=bar+x",
|
|
expected: url.Values{
|
|
"foo": []string{"bar x"},
|
|
},
|
|
},
|
|
{
|
|
name: "url &&",
|
|
query: "foo=bar&&lol=bur",
|
|
expected: url.Values{
|
|
"foo": []string{"bar"},
|
|
"lol": []string{"bur"},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
res := ParseQuery(test.query)
|
|
if !reflect.DeepEqual(res, test.expected) {
|
|
t.Fatalf("unexpected result: %v", res)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestExtractQueryParam(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
query string
|
|
param string
|
|
expected []string
|
|
}{
|
|
{
|
|
name: "Simple uri",
|
|
query: "/foobar/toto?ab=cd&ef=gh",
|
|
param: "ab",
|
|
expected: []string{"cd"},
|
|
},
|
|
{
|
|
name: "Simple uri, repeating param",
|
|
query: "/foobar?foo=bar&foo=baz",
|
|
param: "foo",
|
|
expected: []string{"bar", "baz"},
|
|
},
|
|
{
|
|
name: "Simple uri with semicolon",
|
|
query: "/foobar/toto?ab=cd;ef=gh",
|
|
param: "ab",
|
|
expected: []string{"cd;ef=gh"},
|
|
},
|
|
{
|
|
name: "Simple query no uri",
|
|
query: "foo=bar",
|
|
param: "foo",
|
|
expected: []string{},
|
|
},
|
|
{
|
|
name: "No QS",
|
|
query: "/foobar",
|
|
param: "foo",
|
|
expected: []string{},
|
|
},
|
|
{
|
|
name: "missing param",
|
|
query: "/foobar/toto?ab=cd&ef=gh",
|
|
param: "baz",
|
|
expected: []string{},
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
res := ExtractQueryParam(test.query, test.param)
|
|
if !reflect.DeepEqual(res, test.expected) {
|
|
t.Fatalf("unexpected result: %v", res)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestJA4H(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
name string
|
|
method string
|
|
url string
|
|
cookies map[string]string
|
|
headers map[string]string
|
|
expectedHash string
|
|
}{
|
|
{
|
|
name: "Basic GET - No cookies",
|
|
method: "GET",
|
|
url: "http://example.com",
|
|
cookies: map[string]string{},
|
|
headers: map[string]string{},
|
|
expectedHash: "ge11nn000000_e3b0c44298fc_000000000000_000000000000",
|
|
},
|
|
{
|
|
name: "Basic POST - No cookies",
|
|
method: "POST",
|
|
url: "http://example.com",
|
|
cookies: map[string]string{},
|
|
headers: map[string]string{},
|
|
expectedHash: "po11nn000000_e3b0c44298fc_000000000000_000000000000",
|
|
},
|
|
{
|
|
name: "GET - With cookies",
|
|
method: "GET",
|
|
url: "http://example.com/foobar",
|
|
cookies: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "qux",
|
|
},
|
|
headers: map[string]string{
|
|
"User-Agent": "Mozilla/5.0",
|
|
},
|
|
expectedHash: "ge11cn010000_b8bcd45ac095_bd87575d11f6_d401f362552e",
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
req, err := http.NewRequest(test.method, test.url, http.NoBody)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create request: %s", err)
|
|
}
|
|
|
|
for key, value := range test.cookies {
|
|
req.AddCookie(&http.Cookie{
|
|
Name: key,
|
|
Value: value,
|
|
})
|
|
}
|
|
|
|
for key, value := range test.headers {
|
|
req.Header.Add(key, value)
|
|
}
|
|
|
|
hash, err := JA4H(req)
|
|
require.NoError(t, err)
|
|
|
|
if hash != test.expectedHash {
|
|
t.Fatalf("JA4H returned unexpected hash: %s", hash)
|
|
}
|
|
})
|
|
}
|
|
|
|
}
|