| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- package config_test
- import (
- "errors"
- "io/fs"
- "os"
- "path/filepath"
- "testing"
- "github.com/TecharoHQ/anubis/data"
- . "github.com/TecharoHQ/anubis/lib/config"
- )
- func p[V any](v V) *V { return &v }
- func TestBotValid(t *testing.T) {
- var tests = []struct {
- bot BotConfig
- err error
- name string
- }{
- {
- name: "simple user agent",
- bot: BotConfig{
- Name: "mozilla-ua",
- Action: RuleChallenge,
- UserAgentRegex: p("Mozilla"),
- },
- err: nil,
- },
- {
- name: "simple path",
- bot: BotConfig{
- Name: "well-known-path",
- Action: RuleAllow,
- PathRegex: p("^/.well-known/.*$"),
- },
- err: nil,
- },
- {
- name: "no rule name",
- bot: BotConfig{
- Action: RuleChallenge,
- UserAgentRegex: p("Mozilla"),
- },
- err: ErrBotMustHaveName,
- },
- {
- name: "no rule matcher",
- bot: BotConfig{
- Name: "broken-rule",
- Action: RuleAllow,
- },
- err: ErrBotMustHaveUserAgentOrPath,
- },
- {
- name: "both user-agent and path",
- bot: BotConfig{
- Name: "path-and-user-agent",
- Action: RuleDeny,
- UserAgentRegex: p("Mozilla"),
- PathRegex: p("^/.secret-place/.*$"),
- },
- err: ErrBotMustHaveUserAgentOrPathNotBoth,
- },
- {
- name: "unknown action",
- bot: BotConfig{
- Name: "Unknown action",
- Action: RuleUnknown,
- UserAgentRegex: p("Mozilla"),
- },
- err: ErrUnknownAction,
- },
- {
- name: "invalid user agent regex",
- bot: BotConfig{
- Name: "mozilla-ua",
- Action: RuleChallenge,
- UserAgentRegex: p("a(b"),
- },
- err: ErrInvalidUserAgentRegex,
- },
- {
- name: "invalid path regex",
- bot: BotConfig{
- Name: "mozilla-ua",
- Action: RuleChallenge,
- PathRegex: p("a(b"),
- },
- err: ErrInvalidPathRegex,
- },
- {
- name: "invalid headers regex",
- bot: BotConfig{
- Name: "mozilla-ua",
- Action: RuleChallenge,
- HeadersRegex: map[string]string{
- "Content-Type": "a(b",
- },
- PathRegex: p("a(b"),
- },
- err: ErrInvalidHeadersRegex,
- },
- {
- name: "challenge difficulty too low",
- bot: BotConfig{
- Name: "mozilla-ua",
- Action: RuleChallenge,
- PathRegex: p("Mozilla"),
- Challenge: &ChallengeRules{
- Difficulty: -1,
- Algorithm: "fast",
- },
- },
- err: ErrChallengeDifficultyTooLow,
- },
- {
- name: "challenge difficulty too high",
- bot: BotConfig{
- Name: "mozilla-ua",
- Action: RuleChallenge,
- PathRegex: p("Mozilla"),
- Challenge: &ChallengeRules{
- Difficulty: 420,
- Algorithm: "fast",
- },
- },
- err: ErrChallengeDifficultyTooHigh,
- },
- {
- name: "invalid cidr range",
- bot: BotConfig{
- Name: "mozilla-ua",
- Action: RuleAllow,
- RemoteAddr: []string{"0.0.0.0/33"},
- },
- err: ErrInvalidCIDR,
- },
- {
- name: "only filter by IP range",
- bot: BotConfig{
- Name: "mozilla-ua",
- Action: RuleAllow,
- RemoteAddr: []string{"0.0.0.0/0"},
- },
- err: nil,
- },
- {
- name: "filter by user agent and IP range",
- bot: BotConfig{
- Name: "mozilla-ua",
- Action: RuleAllow,
- UserAgentRegex: p("Mozilla"),
- RemoteAddr: []string{"0.0.0.0/0"},
- },
- err: nil,
- },
- {
- name: "filter by path and IP range",
- bot: BotConfig{
- Name: "mozilla-ua",
- Action: RuleAllow,
- PathRegex: p("^.*$"),
- RemoteAddr: []string{"0.0.0.0/0"},
- },
- err: nil,
- },
- {
- name: "weight rule without weight",
- bot: BotConfig{
- Name: "weight-adjust-if-mozilla",
- Action: RuleWeigh,
- UserAgentRegex: p("Mozilla"),
- },
- },
- {
- name: "weight rule with weight adjust",
- bot: BotConfig{
- Name: "weight-adjust-if-mozilla",
- Action: RuleWeigh,
- UserAgentRegex: p("Mozilla"),
- Weight: &Weight{
- Adjust: 5,
- },
- },
- },
- }
- for _, cs := range tests {
- cs := cs
- t.Run(cs.name, func(t *testing.T) {
- err := cs.bot.Valid()
- if err == nil && cs.err == nil {
- return
- }
- if err == nil && cs.err != nil {
- t.Errorf("didn't get an error, but wanted: %v", cs.err)
- }
- if !errors.Is(err, cs.err) {
- t.Logf("got wrong error from Valid()")
- t.Logf("wanted: %v", cs.err)
- t.Logf("got: %v", err)
- t.Errorf("got invalid error from check")
- }
- })
- }
- }
- func TestConfigValidKnownGood(t *testing.T) {
- finfos, err := os.ReadDir("testdata/good")
- if err != nil {
- t.Fatal(err)
- }
- for _, st := range finfos {
- st := st
- t.Run(st.Name(), func(t *testing.T) {
- fin, err := os.Open(filepath.Join("testdata", "good", st.Name()))
- if err != nil {
- t.Fatal(err)
- }
- defer fin.Close()
- c, err := Load(fin, st.Name())
- if err != nil {
- t.Fatal(err)
- }
- if err := c.Valid(); err != nil {
- t.Error(err)
- }
- if len(c.Bots) == 0 {
- t.Error("wanted more than 0 bots, got zero")
- }
- })
- }
- }
- func TestImportStatement(t *testing.T) {
- type testCase struct {
- err error
- name string
- importPath string
- }
- var tests []testCase
- for _, folderName := range []string{
- "apps",
- "bots",
- "common",
- "crawlers",
- "meta",
- } {
- if err := fs.WalkDir(data.BotPolicies, folderName, func(path string, d fs.DirEntry, err error) error {
- if err != nil {
- return err
- }
- if d.IsDir() {
- return nil
- }
- if d.Name() == "README.md" {
- return nil
- }
- tests = append(tests, testCase{
- name: "(data)/" + path,
- importPath: "(data)/" + path,
- err: nil,
- })
- return nil
- }); err != nil {
- t.Fatal(err)
- }
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- is := &ImportStatement{
- Import: tt.importPath,
- }
- if err := is.Valid(); err != nil {
- t.Errorf("validation error: %v", err)
- }
- if len(is.Bots) == 0 {
- t.Error("wanted bot definitions, but got none")
- }
- })
- }
- }
- func TestConfigValidBad(t *testing.T) {
- finfos, err := os.ReadDir("testdata/bad")
- if err != nil {
- t.Fatal(err)
- }
- for _, st := range finfos {
- st := st
- t.Run(st.Name(), func(t *testing.T) {
- fin, err := os.Open(filepath.Join("testdata", "bad", st.Name()))
- if err != nil {
- t.Fatal(err)
- }
- defer fin.Close()
- _, err = Load(fin, filepath.Join("testdata", "bad", st.Name()))
- if err == nil {
- t.Fatal("validation should have failed but didn't somehow")
- } else {
- t.Log(err)
- }
- })
- }
- }
- func TestBotConfigZero(t *testing.T) {
- var b BotConfig
- if !b.Zero() {
- t.Error("zero value config.BotConfig is not zero value")
- }
- b.Name = "hi"
- if b.Zero() {
- t.Error("config.BotConfig with name is zero value")
- }
- b.UserAgentRegex = p(".*")
- if b.Zero() {
- t.Error("config.BotConfig with user agent regex is zero value")
- }
- b.PathRegex = p(".*")
- if b.Zero() {
- t.Error("config.BotConfig with path regex is zero value")
- }
- b.HeadersRegex = map[string]string{"hi": "there"}
- if b.Zero() {
- t.Error("config.BotConfig with headers regex is zero value")
- }
- b.Action = RuleAllow
- if b.Zero() {
- t.Error("config.BotConfig with action is zero value")
- }
- b.RemoteAddr = []string{"::/0"}
- if b.Zero() {
- t.Error("config.BotConfig with remote addresses is zero value")
- }
- b.Challenge = &ChallengeRules{
- Difficulty: 4,
- Algorithm: DefaultAlgorithm,
- }
- if b.Zero() {
- t.Error("config.BotConfig with challenge rules is zero value")
- }
- }
|