lib: move config to yaml (#307)

* lib: move config to yaml

Signed-off-by: Xe Iaso <me@xeiaso.net>

* web: run go generate

Signed-off-by: Xe Iaso <me@xeiaso.net>

* Add Haiku to known instances (#304)

Signed-off-by: Asmodeus <46908100+AsmodeumX@users.noreply.github.com>

* Add headers bot rule (#300)

* Closes #291: add headers support to bot policy rules

* Fix config validator

* update docs for JSON -> YAML

Signed-off-by: Xe Iaso <me@xeiaso.net>

* docs: document http header based actions

Signed-off-by: Xe Iaso <me@xeiaso.net>

* lib: add missing test

Signed-off-by: Xe Iaso <me@xeiaso.net>

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Xe Iaso <me@xeiaso.net>

---------

Signed-off-by: Xe Iaso <me@xeiaso.net>
Signed-off-by: Asmodeus <46908100+AsmodeumX@users.noreply.github.com>
Co-authored-by: Asmodeus <46908100+AsmodeumX@users.noreply.github.com>
Co-authored-by: Neur0toxine <pashok9825@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Xe Iaso 2025-04-20 20:09:27 -04:00 committed by GitHub
parent 022eb59ff3
commit d40b5cfdab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 854 additions and 19 deletions

View file

@ -90,8 +90,8 @@ func LoadPoliciesOrDefault(fname string, defaultDifficulty int) (*policy.ParsedC
return nil, fmt.Errorf("can't parse policy file %s: %w", fname, err)
}
} else {
fname = "(data)/botPolicies.json"
fin, err = data.BotPolicies.Open("botPolicies.json")
fname = "(data)/botPolicies.yaml"
fin, err = data.BotPolicies.Open("botPolicies.yaml")
if err != nil {
return nil, fmt.Errorf("[unexpected] can't parse builtin policy file %s: %w", fname, err)
}

View file

@ -8,6 +8,7 @@ import (
"testing"
"github.com/TecharoHQ/anubis"
"github.com/TecharoHQ/anubis/data"
"github.com/TecharoHQ/anubis/internal"
"github.com/TecharoHQ/anubis/lib/policy"
)
@ -15,7 +16,7 @@ import (
func loadPolicies(t *testing.T, fname string) *policy.ParsedConfig {
t.Helper()
anubisPolicy, err := LoadPoliciesOrDefault("", anubis.DefaultDifficulty)
anubisPolicy, err := LoadPoliciesOrDefault(fname, anubis.DefaultDifficulty)
if err != nil {
t.Fatal(err)
}
@ -55,6 +56,22 @@ func makeChallenge(t *testing.T, ts *httptest.Server) challenge {
return chall
}
func TestLoadPolicies(t *testing.T) {
for _, fname := range []string{"botPolicies.json", "botPolicies.yaml"} {
t.Run(fname, func(t *testing.T) {
fin, err := data.BotPolicies.Open(fname)
if err != nil {
t.Fatal(err)
}
defer fin.Close()
if _, err := policy.ParseConfig(fin, fname, 4); err != nil {
t.Fatal(err)
}
})
}
}
// Regression test for CVE-2025-24369
func TestCVE2025_24369(t *testing.T) {
pol := loadPolicies(t, "")

View file

@ -1,11 +1,12 @@
package config
import (
"encoding/json"
"errors"
"os"
"path/filepath"
"testing"
"k8s.io/apimachinery/pkg/util/yaml"
)
func p[V any](v V) *V { return &v }
@ -219,7 +220,7 @@ func TestConfigValidKnownGood(t *testing.T) {
defer fin.Close()
var c Config
if err := json.NewDecoder(fin).Decode(&c); err != nil {
if err := yaml.NewYAMLToJSONDecoder(fin).Decode(&c); err != nil {
t.Fatalf("can't decode file: %v", err)
}
@ -246,7 +247,7 @@ func TestConfigValidBad(t *testing.T) {
defer fin.Close()
var c Config
if err := json.NewDecoder(fin).Decode(&c); err != nil {
if err := yaml.NewYAMLToJSONDecoder(fin).Decode(&c); err != nil {
t.Fatalf("can't decode file: %v", err)
}

View file

@ -0,0 +1,7 @@
bots:
- name: path-bad
path_regex: "a(b"
action: DENY
- name: user-agent-bad
user_agent_regex: "a(b"
action: DENY

View file

@ -0,0 +1 @@
bots: []

View file

@ -0,0 +1 @@
{}

View file

@ -0,0 +1,6 @@
bots:
- name: everyones-invited
remote_addresses:
- "0.0.0.0/0"
- "::/0"
action: ALLOW

View file

@ -0,0 +1,5 @@
bots:
- name: cloudflare-workers
headers_regex:
CF-Worker: .*
action: DENY

View file

@ -0,0 +1,4 @@
bots:
- name: generic-browser
user_agent_regex: Mozilla
action: CHALLENGE

View file

@ -0,0 +1,4 @@
bots:
- name: everything
user_agent_regex: .*
action: DENY

View file

@ -1,7 +1,6 @@
package policy
import (
"encoding/json"
"errors"
"fmt"
"io"
@ -11,6 +10,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/yl2chen/cidranger"
"k8s.io/apimachinery/pkg/util/yaml"
"github.com/TecharoHQ/anubis/lib/policy/config"
)
@ -38,8 +38,8 @@ func NewParsedConfig(orig config.Config) *ParsedConfig {
func ParseConfig(fin io.Reader, fname string, defaultDifficulty int) (*ParsedConfig, error) {
var c config.Config
if err := json.NewDecoder(fin).Decode(&c); err != nil {
return nil, fmt.Errorf("can't parse policy config JSON %s: %w", fname, err)
if err := yaml.NewYAMLToJSONDecoder(fin).Decode(&c); err != nil {
return nil, fmt.Errorf("can't parse policy config YAML %s: %w", fname, err)
}
if err := c.Valid(); err != nil {