mirror of
https://github.com/TecharoHQ/anubis.git
synced 2026-04-25 09:32:43 +00:00
feat(config): properly marshal bot policy rules
Signed-off-by: Xe Iaso <me@xeiaso.net>
This commit is contained in:
@@ -46,15 +46,15 @@ const (
|
|||||||
const DefaultAlgorithm = "fast"
|
const DefaultAlgorithm = "fast"
|
||||||
|
|
||||||
type BotConfig struct {
|
type BotConfig struct {
|
||||||
UserAgentRegex *string `json:"user_agent_regex,omitempty"`
|
UserAgentRegex *string `json:"user_agent_regex,omitempty" yaml:"user_agent_regex,omitempty"`
|
||||||
PathRegex *string `json:"path_regex,omitempty"`
|
PathRegex *string `json:"path_regex,omitempty" yaml:"path_regex,omitempty"`
|
||||||
HeadersRegex map[string]string `json:"headers_regex,omitempty"`
|
HeadersRegex map[string]string `json:"headers_regex,omitempty" yaml:"headers_regex,omitempty"`
|
||||||
Expression *ExpressionOrList `json:"expression,omitempty"`
|
Expression *ExpressionOrList `json:"expression,omitempty" yaml:"expression,omitempty"`
|
||||||
Challenge *ChallengeRules `json:"challenge,omitempty"`
|
Challenge *ChallengeRules `json:"challenge,omitempty" yaml:"challenge,omitempty"`
|
||||||
Weight *Weight `json:"weight,omitempty"`
|
Weight *Weight `json:"weight,omitempty" yaml:"weight,omitempty"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name" yaml:"name"`
|
||||||
Action Rule `json:"action"`
|
Action Rule `json:"action" yaml:"action"`
|
||||||
RemoteAddr []string `json:"remote_addresses,omitempty"`
|
RemoteAddr []string `json:"remote_addresses,omitempty" yaml:"remote_addresses,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b BotConfig) Zero() bool {
|
func (b BotConfig) Zero() bool {
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ExpressionOrList struct {
|
type ExpressionOrList struct {
|
||||||
Expression string `json:"-"`
|
Expression string `json:"-" yaml:"-"`
|
||||||
All []string `json:"all,omitempty"`
|
All []string `json:"all,omitempty" yaml:"all,omitempty"`
|
||||||
Any []string `json:"any,omitempty"`
|
Any []string `json:"any,omitempty" yaml:"any,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (eol ExpressionOrList) Equal(rhs *ExpressionOrList) bool {
|
func (eol ExpressionOrList) Equal(rhs *ExpressionOrList) bool {
|
||||||
@@ -34,6 +34,43 @@ func (eol ExpressionOrList) Equal(rhs *ExpressionOrList) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (eol *ExpressionOrList) MarshalYAML() (any, error) {
|
||||||
|
switch {
|
||||||
|
case len(eol.All) == 1 && len(eol.Any) == 0:
|
||||||
|
eol.Expression = eol.All[0]
|
||||||
|
eol.All = nil
|
||||||
|
case len(eol.Any) == 1 && len(eol.All) == 0:
|
||||||
|
eol.Expression = eol.Any[0]
|
||||||
|
eol.Any = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if eol.Expression != "" {
|
||||||
|
return eol.Expression, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type RawExpressionOrList ExpressionOrList
|
||||||
|
return RawExpressionOrList(*eol), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (eol *ExpressionOrList) MarshalJSON() ([]byte, error) {
|
||||||
|
switch {
|
||||||
|
case len(eol.All) == 1 && len(eol.Any) == 0:
|
||||||
|
eol.Expression = eol.All[0]
|
||||||
|
eol.All = nil
|
||||||
|
case len(eol.Any) == 1 && len(eol.All) == 0:
|
||||||
|
eol.Expression = eol.Any[0]
|
||||||
|
eol.Any = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if eol.Expression != "" {
|
||||||
|
return json.Marshal(string(eol.Expression))
|
||||||
|
}
|
||||||
|
|
||||||
|
type RawExpressionOrList ExpressionOrList
|
||||||
|
val := RawExpressionOrList(*eol)
|
||||||
|
return json.Marshal(val)
|
||||||
|
}
|
||||||
|
|
||||||
func (eol *ExpressionOrList) UnmarshalJSON(data []byte) error {
|
func (eol *ExpressionOrList) UnmarshalJSON(data []byte) error {
|
||||||
switch string(data[0]) {
|
switch string(data[0]) {
|
||||||
case `"`: // string
|
case `"`: // string
|
||||||
|
|||||||
@@ -1,12 +1,147 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
yaml "sigs.k8s.io/yaml/goyaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestExpressionOrListUnmarshal(t *testing.T) {
|
func TestExpressionOrListMarshalJSON(t *testing.T) {
|
||||||
|
for _, tt := range []struct {
|
||||||
|
name string
|
||||||
|
input *ExpressionOrList
|
||||||
|
output []byte
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "single expression",
|
||||||
|
input: &ExpressionOrList{
|
||||||
|
Expression: "true",
|
||||||
|
},
|
||||||
|
output: []byte(`"true"`),
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "all",
|
||||||
|
input: &ExpressionOrList{
|
||||||
|
All: []string{"true", "true"},
|
||||||
|
},
|
||||||
|
output: []byte(`{"all":["true","true"]}`),
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "all one",
|
||||||
|
input: &ExpressionOrList{
|
||||||
|
All: []string{"true"},
|
||||||
|
},
|
||||||
|
output: []byte(`"true"`),
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "any",
|
||||||
|
input: &ExpressionOrList{
|
||||||
|
Any: []string{"true", "false"},
|
||||||
|
},
|
||||||
|
output: []byte(`{"any":["true","false"]}`),
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "any one",
|
||||||
|
input: &ExpressionOrList{
|
||||||
|
Any: []string{"true"},
|
||||||
|
},
|
||||||
|
output: []byte(`"true"`),
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result, err := json.Marshal(tt.input)
|
||||||
|
if !errors.Is(err, tt.err) {
|
||||||
|
t.Errorf("wanted marshal error: %v but got: %v", tt.err, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(result, tt.output) {
|
||||||
|
t.Logf("wanted: %s", string(tt.output))
|
||||||
|
t.Logf("got: %s", string(result))
|
||||||
|
t.Error("mismatched output")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExpressionOrListMarshalYAML(t *testing.T) {
|
||||||
|
for _, tt := range []struct {
|
||||||
|
name string
|
||||||
|
input *ExpressionOrList
|
||||||
|
output []byte
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "single expression",
|
||||||
|
input: &ExpressionOrList{
|
||||||
|
Expression: "true",
|
||||||
|
},
|
||||||
|
output: []byte(`"true"`),
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "all",
|
||||||
|
input: &ExpressionOrList{
|
||||||
|
All: []string{"true", "true"},
|
||||||
|
},
|
||||||
|
output: []byte(`all:
|
||||||
|
- "true"
|
||||||
|
- "true"`),
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "all one",
|
||||||
|
input: &ExpressionOrList{
|
||||||
|
All: []string{"true"},
|
||||||
|
},
|
||||||
|
output: []byte(`"true"`),
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "any",
|
||||||
|
input: &ExpressionOrList{
|
||||||
|
Any: []string{"true", "false"},
|
||||||
|
},
|
||||||
|
output: []byte(`any:
|
||||||
|
- "true"
|
||||||
|
- "false"`),
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "any one",
|
||||||
|
input: &ExpressionOrList{
|
||||||
|
Any: []string{"true"},
|
||||||
|
},
|
||||||
|
output: []byte(`"true"`),
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result, err := yaml.Marshal(tt.input)
|
||||||
|
if !errors.Is(err, tt.err) {
|
||||||
|
t.Errorf("wanted marshal error: %v but got: %v", tt.err, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
result = bytes.TrimSpace(result)
|
||||||
|
|
||||||
|
if !bytes.Equal(result, tt.output) {
|
||||||
|
t.Logf("wanted: %q", string(tt.output))
|
||||||
|
t.Logf("got: %q", string(result))
|
||||||
|
t.Error("mismatched output")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExpressionOrListUnmarshalJSON(t *testing.T) {
|
||||||
for _, tt := range []struct {
|
for _, tt := range []struct {
|
||||||
err error
|
err error
|
||||||
validErr error
|
validErr error
|
||||||
|
|||||||
Reference in New Issue
Block a user