split & reorganize tests a bit. Add tests on existing zones (#2925)
This commit is contained in:
parent
26bcd0912a
commit
912c4bca70
5 changed files with 1767 additions and 1454 deletions
714
pkg/acquisition/modules/appsec/appsec_hooks_test.go
Normal file
714
pkg/acquisition/modules/appsec/appsec_hooks_test.go
Normal file
|
@ -0,0 +1,714 @@
|
|||
package appsecacquisition
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/appsec"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/appsec/appsec_rule"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAppsecOnMatchHooks(t *testing.T) {
|
||||
tests := []appsecRuleTest{
|
||||
{
|
||||
name: "no rule : check return code",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.Len(t, responses, 1)
|
||||
require.Equal(t, 403, responses[0].BouncerHTTPResponseCode)
|
||||
require.Equal(t, 403, responses[0].UserHTTPResponseCode)
|
||||
require.Equal(t, appsec.BanRemediation, responses[0].Action)
|
||||
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "on_match: change return code",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
on_match: []appsec.Hook{
|
||||
{Filter: "IsInBand == true", Apply: []string{"SetReturnCode(413)"}},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.Len(t, responses, 1)
|
||||
require.Equal(t, 403, responses[0].BouncerHTTPResponseCode)
|
||||
require.Equal(t, 413, responses[0].UserHTTPResponseCode)
|
||||
require.Equal(t, appsec.BanRemediation, responses[0].Action)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "on_match: change action to a non standard one (log)",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
on_match: []appsec.Hook{
|
||||
{Filter: "IsInBand == true", Apply: []string{"SetRemediation('log')"}},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.Len(t, responses, 1)
|
||||
require.Equal(t, "log", responses[0].Action)
|
||||
require.Equal(t, 403, responses[0].BouncerHTTPResponseCode)
|
||||
require.Equal(t, 403, responses[0].UserHTTPResponseCode)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "on_match: change action to another standard one (allow)",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
on_match: []appsec.Hook{
|
||||
{Filter: "IsInBand == true", Apply: []string{"SetRemediation('allow')"}},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.Len(t, responses, 1)
|
||||
require.Equal(t, appsec.AllowRemediation, responses[0].Action)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "on_match: change action to another standard one (ban)",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
on_match: []appsec.Hook{
|
||||
{Filter: "IsInBand == true", Apply: []string{"SetRemediation('ban')"}},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, responses, 1)
|
||||
//note: SetAction normalizes deny, ban and block to ban
|
||||
require.Equal(t, appsec.BanRemediation, responses[0].Action)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "on_match: change action to another standard one (captcha)",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
on_match: []appsec.Hook{
|
||||
{Filter: "IsInBand == true", Apply: []string{"SetRemediation('captcha')"}},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, responses, 1)
|
||||
//note: SetAction normalizes deny, ban and block to ban
|
||||
require.Equal(t, appsec.CaptchaRemediation, responses[0].Action)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "on_match: change action to a non standard one",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
on_match: []appsec.Hook{
|
||||
{Filter: "IsInBand == true", Apply: []string{"SetRemediation('foobar')"}},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.Len(t, responses, 1)
|
||||
require.Equal(t, "foobar", responses[0].Action)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "on_match: cancel alert",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule42",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
on_match: []appsec.Hook{
|
||||
{Filter: "IsInBand == true && LogInfo('XX -> %s', evt.Appsec.MatchedRules.GetName())", Apply: []string{"CancelAlert()"}},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 1)
|
||||
require.Equal(t, types.LOG, events[0].Type)
|
||||
require.Len(t, responses, 1)
|
||||
require.Equal(t, appsec.BanRemediation, responses[0].Action)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "on_match: cancel event",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule42",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
on_match: []appsec.Hook{
|
||||
{Filter: "IsInBand == true", Apply: []string{"CancelEvent()"}},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 1)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
require.Len(t, responses, 1)
|
||||
require.Equal(t, appsec.BanRemediation, responses[0].Action)
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
loadAppSecEngine(test, t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppsecPreEvalHooks(t *testing.T) {
|
||||
|
||||
tests := []appsecRuleTest{
|
||||
{
|
||||
name: "Basic on_load hook to disable inband rule",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
pre_eval: []appsec.Hook{
|
||||
{Filter: "1 == 1", Apply: []string{"RemoveInBandRuleByName('rule1')"}},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Empty(t, events)
|
||||
require.Len(t, responses, 1)
|
||||
require.False(t, responses[0].InBandInterrupt)
|
||||
require.False(t, responses[0].OutOfBandInterrupt)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Basic on_load fails to disable rule",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
pre_eval: []appsec.Hook{
|
||||
{Filter: "1 ==2", Apply: []string{"RemoveInBandRuleByName('rule1')"}},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.True(t, events[1].Appsec.HasInBandMatches)
|
||||
require.Len(t, events[1].Appsec.MatchedRules, 1)
|
||||
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||
|
||||
require.Len(t, responses, 1)
|
||||
require.True(t, responses[0].InBandInterrupt)
|
||||
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "on_load : disable inband by tag",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rulez",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
pre_eval: []appsec.Hook{
|
||||
{Apply: []string{"RemoveInBandRuleByTag('crowdsec-rulez')"}},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Empty(t, events)
|
||||
require.Len(t, responses, 1)
|
||||
require.False(t, responses[0].InBandInterrupt)
|
||||
require.False(t, responses[0].OutOfBandInterrupt)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "on_load : disable inband by ID",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rulez",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
pre_eval: []appsec.Hook{
|
||||
{Apply: []string{"RemoveInBandRuleByID(1516470898)"}}, //rule ID is generated at runtime. If you change rule, it will break the test (:
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Empty(t, events)
|
||||
require.Len(t, responses, 1)
|
||||
require.False(t, responses[0].InBandInterrupt)
|
||||
require.False(t, responses[0].OutOfBandInterrupt)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "on_load : disable inband by name",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rulez",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
pre_eval: []appsec.Hook{
|
||||
{Apply: []string{"RemoveInBandRuleByName('rulez')"}},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Empty(t, events)
|
||||
require.Len(t, responses, 1)
|
||||
require.False(t, responses[0].InBandInterrupt)
|
||||
require.False(t, responses[0].OutOfBandInterrupt)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "on_load : outofband default behavior",
|
||||
expected_load_ok: true,
|
||||
outofband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rulez",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 1)
|
||||
require.Equal(t, types.LOG, events[0].Type)
|
||||
require.True(t, events[0].Appsec.HasOutBandMatches)
|
||||
require.False(t, events[0].Appsec.HasInBandMatches)
|
||||
require.Len(t, events[0].Appsec.MatchedRules, 1)
|
||||
require.Equal(t, "rulez", events[0].Appsec.MatchedRules[0]["msg"])
|
||||
//maybe surprising, but response won't mention OOB event, as it's sent as soon as the inband phase is over.
|
||||
require.Len(t, responses, 1)
|
||||
require.False(t, responses[0].InBandInterrupt)
|
||||
require.False(t, responses[0].OutOfBandInterrupt)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "on_load : set remediation by tag",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rulez",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
pre_eval: []appsec.Hook{
|
||||
{Apply: []string{"SetRemediationByTag('crowdsec-rulez', 'foobar')"}}, //rule ID is generated at runtime. If you change rule, it will break the test (:
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Len(t, responses, 1)
|
||||
require.Equal(t, "foobar", responses[0].Action)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "on_load : set remediation by name",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rulez",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
pre_eval: []appsec.Hook{
|
||||
{Apply: []string{"SetRemediationByName('rulez', 'foobar')"}}, //rule ID is generated at runtime. If you change rule, it will break the test (:
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Len(t, responses, 1)
|
||||
require.Equal(t, "foobar", responses[0].Action)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "on_load : set remediation by ID",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rulez",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
pre_eval: []appsec.Hook{
|
||||
{Apply: []string{"SetRemediationByID(1516470898, 'foobar')"}}, //rule ID is generated at runtime. If you change rule, it will break the test (:
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Len(t, responses, 1)
|
||||
require.Equal(t, "foobar", responses[0].Action)
|
||||
require.Equal(t, "foobar", appsecResponse.Action)
|
||||
require.Equal(t, http.StatusForbidden, appsecResponse.HTTPStatus)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
loadAppSecEngine(test, t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppsecRemediationConfigHooks(t *testing.T) {
|
||||
|
||||
tests := []appsecRuleTest{
|
||||
{
|
||||
name: "Basic matching rule",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Equal(t, appsec.BanRemediation, responses[0].Action)
|
||||
require.Equal(t, http.StatusForbidden, statusCode)
|
||||
require.Equal(t, appsec.BanRemediation, appsecResponse.Action)
|
||||
require.Equal(t, http.StatusForbidden, appsecResponse.HTTPStatus)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "SetRemediation",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
on_match: []appsec.Hook{{Apply: []string{"SetRemediation('captcha')"}}}, //rule ID is generated at runtime. If you change rule, it will break the test (:
|
||||
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Equal(t, appsec.CaptchaRemediation, responses[0].Action)
|
||||
require.Equal(t, http.StatusForbidden, statusCode)
|
||||
require.Equal(t, appsec.CaptchaRemediation, appsecResponse.Action)
|
||||
require.Equal(t, http.StatusForbidden, appsecResponse.HTTPStatus)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "SetRemediation",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
on_match: []appsec.Hook{{Apply: []string{"SetReturnCode(418)"}}}, //rule ID is generated at runtime. If you change rule, it will break the test (:
|
||||
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Equal(t, appsec.BanRemediation, responses[0].Action)
|
||||
require.Equal(t, http.StatusForbidden, statusCode)
|
||||
require.Equal(t, appsec.BanRemediation, appsecResponse.Action)
|
||||
require.Equal(t, http.StatusTeapot, appsecResponse.HTTPStatus)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
loadAppSecEngine(test, t)
|
||||
})
|
||||
}
|
||||
}
|
||||
func TestOnMatchRemediationHooks(t *testing.T) {
|
||||
tests := []appsecRuleTest{
|
||||
{
|
||||
name: "set remediation to allow with on_match hook",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule42",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
on_match: []appsec.Hook{
|
||||
{Filter: "IsInBand == true", Apply: []string{"SetRemediation('allow')"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Equal(t, appsec.AllowRemediation, appsecResponse.Action)
|
||||
require.Equal(t, http.StatusOK, appsecResponse.HTTPStatus)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "set remediation to captcha + custom user code with on_match hook",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule42",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
DefaultRemediation: appsec.AllowRemediation,
|
||||
on_match: []appsec.Hook{
|
||||
{Filter: "IsInBand == true", Apply: []string{"SetRemediation('captcha')", "SetReturnCode(418)"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
spew.Dump(responses)
|
||||
spew.Dump(appsecResponse)
|
||||
|
||||
log.Errorf("http status : %d", statusCode)
|
||||
require.Equal(t, appsec.CaptchaRemediation, appsecResponse.Action)
|
||||
require.Equal(t, http.StatusTeapot, appsecResponse.HTTPStatus)
|
||||
require.Equal(t, http.StatusForbidden, statusCode)
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
loadAppSecEngine(test, t)
|
||||
})
|
||||
}
|
||||
}
|
320
pkg/acquisition/modules/appsec/appsec_remediation_test.go
Normal file
320
pkg/acquisition/modules/appsec/appsec_remediation_test.go
Normal file
|
@ -0,0 +1,320 @@
|
|||
package appsecacquisition
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/appsec"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/appsec/appsec_rule"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAppsecDefaultPassRemediation(t *testing.T) {
|
||||
|
||||
tests := []appsecRuleTest{
|
||||
{
|
||||
name: "Basic non-matching rule",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/",
|
||||
Args: url.Values{"foo": []string{"tutu"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Equal(t, appsec.AllowRemediation, responses[0].Action)
|
||||
require.Equal(t, http.StatusOK, statusCode)
|
||||
require.Equal(t, appsec.AllowRemediation, appsecResponse.Action)
|
||||
require.Equal(t, http.StatusOK, appsecResponse.HTTPStatus)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "DefaultPassAction: pass",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/",
|
||||
Args: url.Values{"foo": []string{"tutu"}},
|
||||
},
|
||||
DefaultPassAction: "allow",
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Equal(t, appsec.AllowRemediation, responses[0].Action)
|
||||
require.Equal(t, http.StatusOK, statusCode)
|
||||
require.Equal(t, appsec.AllowRemediation, appsecResponse.Action)
|
||||
require.Equal(t, http.StatusOK, appsecResponse.HTTPStatus)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "DefaultPassAction: captcha",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/",
|
||||
Args: url.Values{"foo": []string{"tutu"}},
|
||||
},
|
||||
DefaultPassAction: "captcha",
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Equal(t, appsec.CaptchaRemediation, responses[0].Action)
|
||||
require.Equal(t, http.StatusOK, statusCode) //@tko: body is captcha, but as it's 200, captcha won't be showed to user
|
||||
require.Equal(t, appsec.CaptchaRemediation, appsecResponse.Action)
|
||||
require.Equal(t, http.StatusOK, appsecResponse.HTTPStatus)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "DefaultPassHTTPCode: 200",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/",
|
||||
Args: url.Values{"foo": []string{"tutu"}},
|
||||
},
|
||||
UserPassedHTTPCode: 200,
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Equal(t, appsec.AllowRemediation, responses[0].Action)
|
||||
require.Equal(t, http.StatusOK, statusCode)
|
||||
require.Equal(t, appsec.AllowRemediation, appsecResponse.Action)
|
||||
require.Equal(t, http.StatusOK, appsecResponse.HTTPStatus)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "DefaultPassHTTPCode: 200",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/",
|
||||
Args: url.Values{"foo": []string{"tutu"}},
|
||||
},
|
||||
UserPassedHTTPCode: 418,
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Equal(t, appsec.AllowRemediation, responses[0].Action)
|
||||
require.Equal(t, http.StatusOK, statusCode)
|
||||
require.Equal(t, appsec.AllowRemediation, appsecResponse.Action)
|
||||
require.Equal(t, http.StatusTeapot, appsecResponse.HTTPStatus)
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
loadAppSecEngine(test, t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppsecDefaultRemediation(t *testing.T) {
|
||||
|
||||
tests := []appsecRuleTest{
|
||||
{
|
||||
name: "Basic matching rule",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Equal(t, appsec.BanRemediation, responses[0].Action)
|
||||
require.Equal(t, http.StatusForbidden, statusCode)
|
||||
require.Equal(t, appsec.BanRemediation, appsecResponse.Action)
|
||||
require.Equal(t, http.StatusForbidden, appsecResponse.HTTPStatus)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "default remediation to ban (default)",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule42",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
DefaultRemediation: "ban",
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Equal(t, appsec.BanRemediation, responses[0].Action)
|
||||
require.Equal(t, http.StatusForbidden, statusCode)
|
||||
require.Equal(t, appsec.BanRemediation, appsecResponse.Action)
|
||||
require.Equal(t, http.StatusForbidden, appsecResponse.HTTPStatus)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "default remediation to allow",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule42",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
DefaultRemediation: "allow",
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Equal(t, appsec.AllowRemediation, responses[0].Action)
|
||||
require.Equal(t, http.StatusOK, statusCode)
|
||||
require.Equal(t, appsec.AllowRemediation, appsecResponse.Action)
|
||||
require.Equal(t, http.StatusOK, appsecResponse.HTTPStatus)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "default remediation to captcha",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule42",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
DefaultRemediation: "captcha",
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Equal(t, appsec.CaptchaRemediation, responses[0].Action)
|
||||
require.Equal(t, http.StatusForbidden, statusCode)
|
||||
require.Equal(t, appsec.CaptchaRemediation, appsecResponse.Action)
|
||||
require.Equal(t, http.StatusForbidden, appsecResponse.HTTPStatus)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "custom user HTTP code",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule42",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
UserBlockedHTTPCode: 418,
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Equal(t, appsec.BanRemediation, responses[0].Action)
|
||||
require.Equal(t, http.StatusForbidden, statusCode)
|
||||
require.Equal(t, appsec.BanRemediation, appsecResponse.Action)
|
||||
require.Equal(t, http.StatusTeapot, appsecResponse.HTTPStatus)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "custom remediation + HTTP code",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule42",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
UserBlockedHTTPCode: 418,
|
||||
DefaultRemediation: "foobar",
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Equal(t, "foobar", responses[0].Action)
|
||||
require.Equal(t, http.StatusForbidden, statusCode)
|
||||
require.Equal(t, "foobar", appsecResponse.Action)
|
||||
require.Equal(t, http.StatusTeapot, appsecResponse.HTTPStatus)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
loadAppSecEngine(test, t)
|
||||
})
|
||||
}
|
||||
}
|
733
pkg/acquisition/modules/appsec/appsec_rules_test.go
Normal file
733
pkg/acquisition/modules/appsec/appsec_rules_test.go
Normal file
|
@ -0,0 +1,733 @@
|
|||
package appsecacquisition
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/appsec"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/appsec/appsec_rule"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAppsecRuleMatches(t *testing.T) {
|
||||
|
||||
tests := []appsecRuleTest{
|
||||
{
|
||||
name: "Basic matching rule",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.True(t, events[1].Appsec.HasInBandMatches)
|
||||
require.Len(t, events[1].Appsec.MatchedRules, 1)
|
||||
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||
|
||||
require.Len(t, responses, 1)
|
||||
require.True(t, responses[0].InBandInterrupt)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Basic non-matching rule",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"tutu"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Empty(t, events)
|
||||
require.Len(t, responses, 1)
|
||||
require.False(t, responses[0].InBandInterrupt)
|
||||
require.False(t, responses[0].OutOfBandInterrupt)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "default remediation to allow",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule42",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
DefaultRemediation: "allow",
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Equal(t, appsec.AllowRemediation, responses[0].Action)
|
||||
require.Equal(t, http.StatusOK, statusCode)
|
||||
require.Equal(t, appsec.AllowRemediation, appsecResponse.Action)
|
||||
require.Equal(t, http.StatusOK, appsecResponse.HTTPStatus)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "default remediation to captcha",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule42",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
DefaultRemediation: "captcha",
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Equal(t, appsec.CaptchaRemediation, responses[0].Action)
|
||||
require.Equal(t, http.StatusForbidden, statusCode)
|
||||
require.Equal(t, appsec.CaptchaRemediation, appsecResponse.Action)
|
||||
require.Equal(t, http.StatusForbidden, appsecResponse.HTTPStatus)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no default remediation / custom user HTTP code",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule42",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
UserBlockedHTTPCode: 418,
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Equal(t, appsec.BanRemediation, responses[0].Action)
|
||||
require.Equal(t, http.StatusForbidden, statusCode)
|
||||
require.Equal(t, appsec.BanRemediation, appsecResponse.Action)
|
||||
require.Equal(t, http.StatusTeapot, appsecResponse.HTTPStatus)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no match but try to set remediation to captcha with on_match hook",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule42",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
on_match: []appsec.Hook{
|
||||
{Filter: "IsInBand == true", Apply: []string{"SetRemediation('captcha')"}},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"bla"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Empty(t, events)
|
||||
require.Equal(t, http.StatusOK, statusCode)
|
||||
require.Equal(t, appsec.AllowRemediation, appsecResponse.Action)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no match but try to set user HTTP code with on_match hook",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule42",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
on_match: []appsec.Hook{
|
||||
{Filter: "IsInBand == true", Apply: []string{"SetReturnCode(418)"}},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"bla"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Empty(t, events)
|
||||
require.Equal(t, http.StatusOK, statusCode)
|
||||
require.Equal(t, appsec.AllowRemediation, appsecResponse.Action)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no match but try to set remediation with pre_eval hook",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule42",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
pre_eval: []appsec.Hook{
|
||||
{Filter: "IsInBand == true", Apply: []string{"SetRemediationByName('rule42', 'captcha')"}},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"bla"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Empty(t, events)
|
||||
require.Equal(t, http.StatusOK, statusCode)
|
||||
require.Equal(t, appsec.AllowRemediation, appsecResponse.Action)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
loadAppSecEngine(test, t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppsecRuleTransforms(t *testing.T) {
|
||||
|
||||
log.SetLevel(log.TraceLevel)
|
||||
tests := []appsecRuleTest{
|
||||
{
|
||||
name: "Basic matching rule",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"URI"},
|
||||
Match: appsec_rule.Match{Type: "equals", Value: "/toto"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/toto",
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "lowercase",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"URI"},
|
||||
Match: appsec_rule.Match{Type: "equals", Value: "/toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/TOTO",
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "uppercase",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"URI"},
|
||||
Match: appsec_rule.Match{Type: "equals", Value: "/TOTO"},
|
||||
Transform: []string{"uppercase"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/toto",
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "b64decode",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "equals", Value: "toto"},
|
||||
Transform: []string{"b64decode"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/?foo=dG90bw",
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "b64decode with extra padding",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "equals", Value: "toto"},
|
||||
Transform: []string{"b64decode"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/?foo=dG90bw===",
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "length",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "gte", Value: "3"},
|
||||
Transform: []string{"length"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/?foo=toto",
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "urldecode",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "equals", Value: "BB/A"},
|
||||
Transform: []string{"urldecode"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/?foo=%42%42%2F%41",
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "trim",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "equals", Value: "BB/A"},
|
||||
Transform: []string{"urldecode", "trim"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/?foo=%20%20%42%42%2F%41%20%20",
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
loadAppSecEngine(test, t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppsecRuleZones(t *testing.T) {
|
||||
|
||||
log.SetLevel(log.TraceLevel)
|
||||
tests := []appsecRuleTest{
|
||||
{
|
||||
name: "rule: ARGS",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS"},
|
||||
Match: appsec_rule.Match{Type: "equals", Value: "toto"},
|
||||
},
|
||||
{
|
||||
Name: "rule2",
|
||||
Zones: []string{"ARGS"},
|
||||
Match: appsec_rule.Match{Type: "equals", Value: "foobar"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/foobar?something=toto&foobar=smth",
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rule: ARGS_NAMES",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"ARGS_NAMES"},
|
||||
Match: appsec_rule.Match{Type: "equals", Value: "toto"},
|
||||
},
|
||||
{
|
||||
Name: "rule2",
|
||||
Zones: []string{"ARGS_NAMES"},
|
||||
Match: appsec_rule.Match{Type: "equals", Value: "foobar"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/foobar?something=toto&foobar=smth",
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.Equal(t, "rule2", events[1].Appsec.MatchedRules[0]["msg"])
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rule: BODY_ARGS",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"BODY_ARGS"},
|
||||
Match: appsec_rule.Match{Type: "equals", Value: "toto"},
|
||||
},
|
||||
{
|
||||
Name: "rule2",
|
||||
Zones: []string{"BODY_ARGS"},
|
||||
Match: appsec_rule.Match{Type: "equals", Value: "foobar"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/",
|
||||
Body: []byte("smth=toto&foobar=other"),
|
||||
Headers: http.Header{"Content-Type": []string{"application/x-www-form-urlencoded"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rule: BODY_ARGS_NAMES",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"BODY_ARGS_NAMES"},
|
||||
Match: appsec_rule.Match{Type: "equals", Value: "toto"},
|
||||
},
|
||||
{
|
||||
Name: "rule2",
|
||||
Zones: []string{"BODY_ARGS_NAMES"},
|
||||
Match: appsec_rule.Match{Type: "equals", Value: "foobar"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/",
|
||||
Body: []byte("smth=toto&foobar=other"),
|
||||
Headers: http.Header{"Content-Type": []string{"application/x-www-form-urlencoded"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.Equal(t, "rule2", events[1].Appsec.MatchedRules[0]["msg"])
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rule: HEADERS",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"HEADERS"},
|
||||
Match: appsec_rule.Match{Type: "equals", Value: "toto"},
|
||||
},
|
||||
{
|
||||
Name: "rule2",
|
||||
Zones: []string{"HEADERS"},
|
||||
Match: appsec_rule.Match{Type: "equals", Value: "foobar"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/",
|
||||
Headers: http.Header{"foobar": []string{"toto"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rule: HEADERS_NAMES",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"HEADERS_NAMES"},
|
||||
Match: appsec_rule.Match{Type: "equals", Value: "toto"},
|
||||
},
|
||||
{
|
||||
Name: "rule2",
|
||||
Zones: []string{"HEADERS_NAMES"},
|
||||
Match: appsec_rule.Match{Type: "equals", Value: "foobar"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/",
|
||||
Headers: http.Header{"foobar": []string{"toto"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.Equal(t, "rule2", events[1].Appsec.MatchedRules[0]["msg"])
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rule: METHOD",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"METHOD"},
|
||||
Match: appsec_rule.Match{Type: "equals", Value: "GET"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/",
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rule: PROTOCOL",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"PROTOCOL"},
|
||||
Match: appsec_rule.Match{Type: "contains", Value: "3.1"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/",
|
||||
Proto: "HTTP/3.1",
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rule: URI",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"URI"},
|
||||
Match: appsec_rule.Match{Type: "equals", Value: "/foobar"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/foobar",
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rule: URI_FULL",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"URI_FULL"},
|
||||
Match: appsec_rule.Match{Type: "equals", Value: "/foobar?a=b"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/foobar?a=b",
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rule: RAW_BODY",
|
||||
expected_load_ok: true,
|
||||
inband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule1",
|
||||
Zones: []string{"RAW_BODY"},
|
||||
Match: appsec_rule.Match{Type: "equals", Value: "foobar=42421"},
|
||||
},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/",
|
||||
Body: []byte("foobar=42421"),
|
||||
Headers: http.Header{"Content-Type": []string{"application/x-www-form-urlencoded"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.APPSEC, events[0].Type)
|
||||
require.Equal(t, types.LOG, events[1].Type)
|
||||
require.Equal(t, "rule1", events[1].Appsec.MatchedRules[0]["msg"])
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
loadAppSecEngine(test, t)
|
||||
})
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue