* fix our behavior to comply more with modsec, REQUEST_URI should be: path+query string * fix #2891 as well * add new transforms * add transform tests
This commit is contained in:
parent
be97466809
commit
63bd31b471
5 changed files with 333 additions and 6 deletions
74
pkg/acquisition/modules/appsec/appsec_others_test.go
Normal file
74
pkg/acquisition/modules/appsec/appsec_others_test.go
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
//go:build !windows
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package appsecacquisition
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/appsec"
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/appsec/appsec_rule"
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAppsecRuleTransformsOthers(t *testing.T) {
|
||||||
|
|
||||||
|
log.SetLevel(log.TraceLevel)
|
||||||
|
tests := []appsecRuleTest{
|
||||||
|
{
|
||||||
|
name: "normalizepath",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "b/c"},
|
||||||
|
Transform: []string{"normalizepath"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/?foo=a/../b/c",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "normalizepath #2",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "b/c/"},
|
||||||
|
Transform: []string{"normalizepath"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/?foo=a/../b/c/////././././",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
loadAppSecEngine(test, t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1284,6 +1284,206 @@ func TestAppsecRuleMatches(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAppsecRuleTransforms(t *testing.T) {
|
||||||
|
|
||||||
|
log.SetLevel(log.TraceLevel)
|
||||||
|
tests := []appsecRuleTest{
|
||||||
|
{
|
||||||
|
name: "Basic matching rule",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"URI"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "/toto"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/toto",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "lowercase",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"URI"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "/toto"},
|
||||||
|
Transform: []string{"lowercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/TOTO",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "uppercase",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"URI"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "/TOTO"},
|
||||||
|
Transform: []string{"uppercase"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/toto",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "b64decode",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "toto"},
|
||||||
|
Transform: []string{"b64decode"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/?foo=dG90bw",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "b64decode with extra padding",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "toto"},
|
||||||
|
Transform: []string{"b64decode"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/?foo=dG90bw===",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "length",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "gte", Value: "3"},
|
||||||
|
Transform: []string{"length"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/?foo=toto",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "urldecode",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "BB/A"},
|
||||||
|
Transform: []string{"urldecode"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/?foo=%42%42%2F%41",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "trim",
|
||||||
|
expected_load_ok: true,
|
||||||
|
inband_rules: []appsec_rule.CustomRule{
|
||||||
|
{
|
||||||
|
Name: "rule1",
|
||||||
|
Zones: []string{"ARGS"},
|
||||||
|
Variables: []string{"foo"},
|
||||||
|
Match: appsec_rule.Match{Type: "equals", Value: "BB/A"},
|
||||||
|
Transform: []string{"urldecode", "trim"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input_request: appsec.ParsedRequest{
|
||||||
|
RemoteAddr: "1.2.3.4",
|
||||||
|
Method: "GET",
|
||||||
|
URI: "/?foo=%20%20%42%42%2F%41%20%20",
|
||||||
|
},
|
||||||
|
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
require.Len(t, events, 2)
|
||||||
|
require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
loadAppSecEngine(test, t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func loadAppSecEngine(test appsecRuleTest, t *testing.T) {
|
func loadAppSecEngine(test appsecRuleTest, t *testing.T) {
|
||||||
if testing.Verbose() {
|
if testing.Verbose() {
|
||||||
log.SetLevel(log.TraceLevel)
|
log.SetLevel(log.TraceLevel)
|
||||||
|
|
46
pkg/acquisition/modules/appsec/appsec_win_test.go
Normal file
46
pkg/acquisition/modules/appsec/appsec_win_test.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package appsecacquisition
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAppsecRuleTransformsWindows(t *testing.T) {
|
||||||
|
|
||||||
|
log.SetLevel(log.TraceLevel)
|
||||||
|
tests := []appsecRuleTest{
|
||||||
|
// {
|
||||||
|
// name: "normalizepath",
|
||||||
|
// expected_load_ok: true,
|
||||||
|
// inband_rules: []appsec_rule.CustomRule{
|
||||||
|
// {
|
||||||
|
// Name: "rule1",
|
||||||
|
// Zones: []string{"ARGS"},
|
||||||
|
// Variables: []string{"foo"},
|
||||||
|
// Match: appsec_rule.Match{Type: "equals", Value: "b/c"},
|
||||||
|
// Transform: []string{"normalizepath"},
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// input_request: appsec.ParsedRequest{
|
||||||
|
// RemoteAddr: "1.2.3.4",
|
||||||
|
// Method: "GET",
|
||||||
|
// URI: "/?foo=a/../b/c",
|
||||||
|
// },
|
||||||
|
// output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||||
|
// require.Len(t, events, 2)
|
||||||
|
// require.Equal(t, types.APPSEC, events[0].Type)
|
||||||
|
// require.Equal(t, types.LOG, events[1].Type)
|
||||||
|
// require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
loadAppSecEngine(test, t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,7 +19,8 @@ var zonesMap map[string]string = map[string]string{
|
||||||
"HEADERS": "REQUEST_HEADERS",
|
"HEADERS": "REQUEST_HEADERS",
|
||||||
"METHOD": "REQUEST_METHOD",
|
"METHOD": "REQUEST_METHOD",
|
||||||
"PROTOCOL": "REQUEST_PROTOCOL",
|
"PROTOCOL": "REQUEST_PROTOCOL",
|
||||||
"URI": "REQUEST_URI",
|
"URI": "REQUEST_FILENAME",
|
||||||
|
"URI_FULL": "REQUEST_URI",
|
||||||
"RAW_BODY": "REQUEST_BODY",
|
"RAW_BODY": "REQUEST_BODY",
|
||||||
"FILENAMES": "FILES",
|
"FILENAMES": "FILES",
|
||||||
}
|
}
|
||||||
|
@ -28,8 +29,14 @@ var transformMap map[string]string = map[string]string{
|
||||||
"lowercase": "t:lowercase",
|
"lowercase": "t:lowercase",
|
||||||
"uppercase": "t:uppercase",
|
"uppercase": "t:uppercase",
|
||||||
"b64decode": "t:base64Decode",
|
"b64decode": "t:base64Decode",
|
||||||
"hexdecode": "t:hexDecode",
|
//"hexdecode": "t:hexDecode", -> not supported by coraza
|
||||||
"length": "t:length",
|
"length": "t:length",
|
||||||
|
"urldecode": "t:urlDecode",
|
||||||
|
"trim": "t:trim",
|
||||||
|
"normalize_path": "t:normalizePath",
|
||||||
|
"normalizepath": "t:normalizePath",
|
||||||
|
"htmlentitydecode": "t:htmlEntityDecode",
|
||||||
|
"html_entity_decode": "t:htmlEntityDecode",
|
||||||
}
|
}
|
||||||
|
|
||||||
var matchMap map[string]string = map[string]string{
|
var matchMap map[string]string = map[string]string{
|
||||||
|
|
|
@ -365,11 +365,11 @@ func NewParsedRequestFromRequest(r *http.Request, logger *logrus.Entry) (ParsedR
|
||||||
UUID: uuid.New().String(),
|
UUID: uuid.New().String(),
|
||||||
ClientHost: clientHost,
|
ClientHost: clientHost,
|
||||||
ClientIP: clientIP,
|
ClientIP: clientIP,
|
||||||
URI: parsedURL.Path,
|
URI: clientURI,
|
||||||
Method: clientMethod,
|
Method: clientMethod,
|
||||||
Host: r.Host,
|
Host: clientHost,
|
||||||
Headers: r.Header,
|
Headers: r.Header,
|
||||||
URL: r.URL,
|
URL: parsedURL,
|
||||||
Proto: r.Proto,
|
Proto: r.Proto,
|
||||||
Body: body,
|
Body: body,
|
||||||
Args: ParseQuery(parsedURL.RawQuery),
|
Args: ParseQuery(parsedURL.RawQuery),
|
||||||
|
|
Loading…
Reference in a new issue