chore(lib/policy): move Checker to its own package to avoid import cycles

Signed-off-by: Xe Iaso <me@xeiaso.net>
This commit is contained in:
Xe Iaso
2025-05-22 11:19:01 -04:00
parent 9bb38d6ad0
commit 86ee5697f3
13 changed files with 218 additions and 58 deletions

44
lib/policy/config/asn.go Normal file
View File

@@ -0,0 +1,44 @@
package config
import (
"errors"
"fmt"
)
var (
ErrPrivateASN = errors.New("bot.ASNs: you have specified a private use ASN")
)
type ASNs struct {
Match []uint32 `json:"match"`
}
func (a *ASNs) Valid() error {
var errs []error
for _, asn := range a.Match {
if isPrivateASN(asn) {
errs = append(errs, fmt.Errorf("%w: %d is private (see RFC 6996)", ErrPrivateASN, asn))
}
}
if len(errs) != 0 {
return fmt.Errorf("bot.ASNs: invalid ASN settings: %w", errors.Join(errs...))
}
return nil
}
// isPrivateASN checks if an ASN is in the private use area.
//
// Based on RFC 6996 and IANA allocations.
func isPrivateASN(asn uint32) bool {
switch {
case asn >= 64512 && asn <= 65534:
return true
case asn >= 4200000000 && asn <= 4294967294:
return true
default:
return false
}
}

View File

@@ -51,14 +51,16 @@ const (
)
type BotConfig struct {
UserAgentRegex *string `json:"user_agent_regex"`
PathRegex *string `json:"path_regex"`
HeadersRegex map[string]string `json:"headers_regex"`
Expression *ExpressionOrList `json:"expression"`
UserAgentRegex *string `json:"user_agent_regex,omitempty"`
PathRegex *string `json:"path_regex,omitempty"`
HeadersRegex map[string]string `json:"headers_regex,omitempty"`
Expression *ExpressionOrList `json:"expression,omitempty"`
Challenge *ChallengeRules `json:"challenge,omitempty"`
GeoIP *GeoIP `json:"geoip,omitempty"`
ASNs *ASNs `json:"asns,omitempty"`
Name string `json:"name"`
Action Rule `json:"action"`
RemoteAddr []string `json:"remote_addresses"`
RemoteAddr []string `json:"remote_addresses,omitempty"`
}
func (b BotConfig) Zero() bool {

View File

@@ -0,0 +1,36 @@
package config
import (
"errors"
"fmt"
"regexp"
"strings"
)
var (
countryCodeRegexp = regexp.MustCompile(`^\w{2}$`)
ErrNotCountryCode = errors.New("config.Bot: invalid country code")
)
type GeoIP struct {
Countries []string `json:"countries"`
}
func (g *GeoIP) Valid() error {
var errs []error
for i, cc := range g.Countries {
if !countryCodeRegexp.MatchString(cc) {
errs = append(errs, fmt.Errorf("%w: %s", ErrNotCountryCode, cc))
}
g.Countries[i] = strings.ToLower(cc)
}
if len(errs) != 0 {
return fmt.Errorf("bot.GeoIP: invalid GeoIP settings: %w", errors.Join(errs...))
}
return nil
}

View File

@@ -0,0 +1,33 @@
package config
import (
"errors"
"testing"
)
func TestGeoIPValid(t *testing.T) {
for _, cs := range []struct {
name string
countries []string
err error
}{
{
name: "basic-working",
countries: []string{"US", "Ca", "mx"},
err: nil,
},
} {
t.Run(cs.name, func(t *testing.T) {
g := &GeoIP{
Countries: cs.countries,
}
err := g.Valid()
if !errors.Is(err, cs.err) {
t.Fatalf("wanted error %v but got: %v", cs.err, err)
}
if err == nil && cs.err != nil {
t.Fatalf("wanted error %v but got none", cs.err)
}
})
}
}