Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cmd/anubis/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"github.com/TecharoHQ/anubis/data"
"github.com/TecharoHQ/anubis/internal"
libanubis "github.com/TecharoHQ/anubis/lib"
"github.com/TecharoHQ/anubis/lib/checker/headerexists"

Check failure

Code scanning / check-spelling

Unrecognized Spelling Error

headerexists is not a recognized word. (unrecognized-spelling)
botPolicy "github.com/TecharoHQ/anubis/lib/policy"
"github.com/TecharoHQ/anubis/lib/policy/config"
"github.com/TecharoHQ/anubis/lib/thoth"
Expand Down Expand Up @@ -323,7 +324,7 @@
if *debugBenchmarkJS {
policy.Bots = []botPolicy.Bot{{
Name: "",
Rules: botPolicy.NewHeaderExistsChecker("User-Agent"),
Rules: headerexists.New("User-Agent"),

Check failure

Code scanning / check-spelling

Unrecognized Spelling Error

headerexists is not a recognized word. (unrecognized-spelling)
Action: config.RuleBenchmark,
}}
}
Expand Down
21 changes: 11 additions & 10 deletions cmd/robots2policy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"regexp"
"strings"

"github.com/TecharoHQ/anubis/lib/checker/expression"
"github.com/TecharoHQ/anubis/lib/policy/config"

"sigs.k8s.io/yaml"
Expand All @@ -37,11 +38,11 @@ type RobotsRule struct {
}

type AnubisRule struct {
Expression *config.ExpressionOrList `yaml:"expression,omitempty" json:"expression,omitempty"`
Challenge *config.ChallengeRules `yaml:"challenge,omitempty" json:"challenge,omitempty"`
Weight *config.Weight `yaml:"weight,omitempty" json:"weight,omitempty"`
Name string `yaml:"name" json:"name"`
Action string `yaml:"action" json:"action"`
Expression *expression.Config `yaml:"expression,omitempty" json:"expression,omitempty"`
Challenge *config.ChallengeRules `yaml:"challenge,omitempty" json:"challenge,omitempty"`
Weight *config.Weight `yaml:"weight,omitempty" json:"weight,omitempty"`
Name string `yaml:"name" json:"name"`
Action string `yaml:"action" json:"action"`
}

func init() {
Expand Down Expand Up @@ -224,11 +225,11 @@ func convertToAnubisRules(robotsRules []RobotsRule) []AnubisRule {
}

if userAgent == "*" {
rule.Expression = &config.ExpressionOrList{
rule.Expression = &expression.Config{
All: []string{"true"}, // Always applies
}
} else {
rule.Expression = &config.ExpressionOrList{
rule.Expression = &expression.Config{
All: []string{fmt.Sprintf("userAgent.contains(%q)", userAgent)},
}
}
Expand All @@ -249,11 +250,11 @@ func convertToAnubisRules(robotsRules []RobotsRule) []AnubisRule {
rule.Name = fmt.Sprintf("%s-global-restriction-%d", *policyName, ruleCounter)
rule.Action = "WEIGH"
rule.Weight = &config.Weight{Adjust: 20} // Increase difficulty significantly
rule.Expression = &config.ExpressionOrList{
rule.Expression = &expression.Config{
All: []string{"true"}, // Always applies
}
} else {
rule.Expression = &config.ExpressionOrList{
rule.Expression = &expression.Config{
All: []string{fmt.Sprintf("userAgent.contains(%q)", userAgent)},
}
}
Expand Down Expand Up @@ -285,7 +286,7 @@ func convertToAnubisRules(robotsRules []RobotsRule) []AnubisRule {
pathCondition := buildPathCondition(disallow)
conditions = append(conditions, pathCondition)

rule.Expression = &config.ExpressionOrList{
rule.Expression = &expression.Config{
All: conditions,
}

Expand Down
7 changes: 7 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package anubis

import "errors"

var (
ErrMisconfiguration = errors.New("[unexpected] policy: administrator misconfiguration")
)
12 changes: 7 additions & 5 deletions lib/anubis.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,17 @@ import (
"github.com/TecharoHQ/anubis/internal/dnsbl"
"github.com/TecharoHQ/anubis/internal/ogtags"
"github.com/TecharoHQ/anubis/lib/challenge"
"github.com/TecharoHQ/anubis/lib/checker"
"github.com/TecharoHQ/anubis/lib/localization"
"github.com/TecharoHQ/anubis/lib/policy"
"github.com/TecharoHQ/anubis/lib/policy/checker"
"github.com/TecharoHQ/anubis/lib/policy/config"
"github.com/TecharoHQ/anubis/lib/store"

// checker implementations
_ "github.com/TecharoHQ/anubis/lib/checker/all"

// challenge implementations
_ "github.com/TecharoHQ/anubis/lib/challenge/metarefresh"
_ "github.com/TecharoHQ/anubis/lib/challenge/proofofwork"
_ "github.com/TecharoHQ/anubis/lib/challenge/all"
)

var (
Expand Down Expand Up @@ -549,7 +551,7 @@ func (s *Server) check(r *http.Request) (policy.CheckResult, *policy.Bot, error)
if matches {
return cr("threshold/"+t.Name, t.Action, weight), &policy.Bot{
Challenge: t.Challenge,
Rules: &checker.List{},
Rules: &checker.Any{},
}, nil
}
}
Expand All @@ -560,6 +562,6 @@ func (s *Server) check(r *http.Request) (policy.CheckResult, *policy.Bot, error)
ReportAs: s.policy.DefaultDifficulty,
Algorithm: config.DefaultAlgorithm,
},
Rules: &checker.List{},
Rules: &checker.Any{},
}, nil
}
6 changes: 6 additions & 0 deletions lib/challenge/all/all.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package all

import (
_ "github.com/TecharoHQ/anubis/lib/challenge/metarefresh"
_ "github.com/TecharoHQ/anubis/lib/challenge/proofofwork"
)
35 changes: 35 additions & 0 deletions lib/checker/all.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package checker

import (
"fmt"
"net/http"
"strings"

"github.com/TecharoHQ/anubis/internal"
)

type All []Interface

func (a All) Check(r *http.Request) (bool, error) {
for _, c := range a {
match, err := c.Check(r)
if err != nil {
return match, err
}
if !match {
return false, err // no match
}
}

return true, nil // match
}

func (a All) Hash() string {
var sb strings.Builder

for _, c := range a {
fmt.Fprintln(&sb, c.Hash())
}

return internal.FastHash(sb.String())
}
10 changes: 10 additions & 0 deletions lib/checker/all/all.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Package all imports all of the standard checker types.
package all

import (
_ "github.com/TecharoHQ/anubis/lib/checker/expression"
_ "github.com/TecharoHQ/anubis/lib/checker/headerexists"

Check failure

Code scanning / check-spelling

Unrecognized Spelling Error

headerexists is not a recognized word. (unrecognized-spelling)
_ "github.com/TecharoHQ/anubis/lib/checker/headermatches"

Check failure

Code scanning / check-spelling

Unrecognized Spelling Error

headermatches is not a recognized word. (unrecognized-spelling)
_ "github.com/TecharoHQ/anubis/lib/checker/path"
_ "github.com/TecharoHQ/anubis/lib/checker/remoteaddress"

Check failure

Code scanning / check-spelling

Unrecognized Spelling Error

remoteaddress is not a recognized word. (unrecognized-spelling)
)
70 changes: 70 additions & 0 deletions lib/checker/all_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package checker

import (
"net/http"
"testing"
)

func TestAll_Check(t *testing.T) {
tests := []struct {
name string
checkers []MockChecker
want bool
wantErr bool
}{
{
name: "All match",
checkers: []MockChecker{
{Result: true, Err: nil},
{Result: true, Err: nil},
},
want: true,
wantErr: false,
},
{
name: "One not match",
checkers: []MockChecker{
{Result: true, Err: nil},
{Result: false, Err: nil},
},
want: false,
wantErr: false,
},
{
name: "No match",
checkers: []MockChecker{
{Result: false, Err: nil},
{Result: false, Err: nil},
},
want: false,
wantErr: false,
},
{
name: "Error encountered",
checkers: []MockChecker{
{Result: true, Err: nil},
{Result: false, Err: http.ErrNotSupported},
},
want: false,
wantErr: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var all All
for _, mc := range tt.checkers {
all = append(all, mc)
}

got, err := all.Check(nil)
if (err != nil) != tt.wantErr {
t.Errorf("All.Check() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("All.Check() = %v, want %v", got, tt.want)
}
})
}
}
35 changes: 35 additions & 0 deletions lib/checker/any.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package checker

import (
"fmt"
"net/http"
"strings"

"github.com/TecharoHQ/anubis/internal"
)

type Any []Interface

func (a Any) Check(r *http.Request) (bool, error) {
for _, c := range a {
match, err := c.Check(r)
if err != nil {
return match, err
}
if match {
return true, err // match
}
}

return false, nil // no match
}

func (a Any) Hash() string {
var sb strings.Builder

for _, c := range a {
fmt.Fprintln(&sb, c.Hash())
}

return internal.FastHash(sb.String())
}
83 changes: 83 additions & 0 deletions lib/checker/any_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package checker

import (
"net/http"
"testing"
)

type MockChecker struct {
Result bool
Err error
}

func (m MockChecker) Check(r *http.Request) (bool, error) {
return m.Result, m.Err
}

func (m MockChecker) Hash() string {
return "mock-hash"
}

func TestAny_Check(t *testing.T) {
tests := []struct {
name string
checkers []MockChecker
want bool
wantErr bool
}{
{
name: "All match",
checkers: []MockChecker{
{Result: true, Err: nil},
{Result: true, Err: nil},
},
want: true,
wantErr: false,
},
{
name: "One match",
checkers: []MockChecker{
{Result: false, Err: nil},
{Result: true, Err: nil},
},
want: true,
wantErr: false,
},
{
name: "No match",
checkers: []MockChecker{
{Result: false, Err: nil},
{Result: false, Err: nil},
},
want: false,
wantErr: false,
},
{
name: "Error encountered",
checkers: []MockChecker{
{Result: false, Err: nil},
{Result: false, Err: http.ErrNotSupported},
},
want: false,
wantErr: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var any Any
for _, mc := range tt.checkers {
any = append(any, mc)
}

got, err := any.Check(nil)
if (err != nil) != tt.wantErr {
t.Errorf("Any.Check() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("Any.Check() = %v, want %v", got, tt.want)
}
})
}
}
17 changes: 17 additions & 0 deletions lib/checker/checker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Package checker defines the Checker interface and a helper utility to avoid import cycles.
package checker

import (
"errors"
"net/http"
)

var (
ErrUnparseableConfig = errors.New("checker: config is unparseable")
ErrInvalidConfig = errors.New("checker: config is invalid")
)

type Interface interface {
Check(*http.Request) (matches bool, err error)
Hash() string
}
File renamed without changes.
Loading
Loading