aboutsummaryrefslogtreecommitdiff
path: root/lib/anubis.go
diff options
context:
space:
mode:
authorXe Iaso <me@xeiaso.net>2025-04-22 07:49:41 -0400
committerGitHub <noreply@github.com>2025-04-22 07:49:41 -0400
commit84b28760b3b54c7d26ad40a1e7343d6de242ad9b (patch)
tree3104e239da1ac3cedc43ee07d58bb6ad5cc9f12c /lib/anubis.go
parent9b7bf8ee06fd84f3aa3298f483d8bca69c3372d4 (diff)
downloadanubis-84b28760b3b54c7d26ad40a1e7343d6de242ad9b.tar.xz
anubis-84b28760b3b54c7d26ad40a1e7343d6de242ad9b.zip
feat(lib): use Checker type instead of ad-hoc logic (#318)
This makes each check into its own type that has encapsulated check logic, meaning that it's easier to add new checker implementations in the future. Signed-off-by: Xe Iaso <me@xeiaso.net>
Diffstat (limited to 'lib/anubis.go')
-rw-r--r--lib/anubis.go83
1 files changed, 18 insertions, 65 deletions
diff --git a/lib/anubis.go b/lib/anubis.go
index afc3d86..82e69d0 100644
--- a/lib/anubis.go
+++ b/lib/anubis.go
@@ -8,7 +8,6 @@ import (
"encoding/json"
"fmt"
"io"
- "log"
"log/slog"
"math"
"net"
@@ -238,12 +237,8 @@ func (s *Server) MaybeReverseProxy(w http.ResponseWriter, r *http.Request) {
templ.Handler(web.Base("Oh noes!", web.ErrorPage("Other internal server error (contact the admin)", s.opts.WebmasterEmail)), templ.WithStatus(http.StatusInternalServerError)).ServeHTTP(w, r)
return
}
- hash, err := rule.Hash()
- if err != nil {
- lg.Error("can't calculate checksum of rule", "err", err)
- templ.Handler(web.Base("Oh noes!", web.ErrorPage("Other internal server error (contact the admin)", s.opts.WebmasterEmail)), templ.WithStatus(http.StatusInternalServerError)).ServeHTTP(w, r)
- return
- }
+ hash := rule.Hash()
+
lg.Debug("rule hash", "hash", hash)
templ.Handler(web.Base("Oh noes!", web.ErrorPage(fmt.Sprintf("Access Denied: error code %s", hash), s.opts.WebmasterEmail)), templ.WithStatus(http.StatusOK)).ServeHTTP(w, r)
return
@@ -337,7 +332,7 @@ func (s *Server) MaybeReverseProxy(w http.ResponseWriter, r *http.Request) {
s.next.ServeHTTP(w, r)
}
-func (s *Server) RenderIndex(w http.ResponseWriter, r *http.Request, cr CheckResult, rule *policy.Bot) {
+func (s *Server) RenderIndex(w http.ResponseWriter, r *http.Request, cr policy.CheckResult, rule *policy.Bot) {
lg := slog.With(
"user_agent", r.UserAgent(),
"accept_language", r.Header.Get("Accept-Language"),
@@ -518,41 +513,33 @@ func (s *Server) TestError(w http.ResponseWriter, r *http.Request) {
templ.Handler(web.Base("Oh noes!", web.ErrorPage(err, s.opts.WebmasterEmail)), templ.WithStatus(http.StatusInternalServerError)).ServeHTTP(w, r)
}
+func cr(name string, rule config.Rule) policy.CheckResult {
+ return policy.CheckResult{
+ Name: name,
+ Rule: rule,
+ }
+}
+
// Check evaluates the list of rules, and returns the result
-func (s *Server) check(r *http.Request) (CheckResult, *policy.Bot, error) {
+func (s *Server) check(r *http.Request) (policy.CheckResult, *policy.Bot, error) {
host := r.Header.Get("X-Real-Ip")
if host == "" {
- return decaymap.Zilch[CheckResult](), nil, fmt.Errorf("[misconfiguration] X-Real-Ip header is not set")
+ return decaymap.Zilch[policy.CheckResult](), nil, fmt.Errorf("[misconfiguration] X-Real-Ip header is not set")
}
addr := net.ParseIP(host)
if addr == nil {
- return decaymap.Zilch[CheckResult](), nil, fmt.Errorf("[misconfiguration] %q is not an IP address", host)
+ return decaymap.Zilch[policy.CheckResult](), nil, fmt.Errorf("[misconfiguration] %q is not an IP address", host)
}
for _, b := range s.policy.Bots {
- if b.UserAgent != nil {
- if b.UserAgent.MatchString(r.UserAgent()) && s.checkRemoteAddress(b, addr) {
- return cr("bot/"+b.Name, b.Action), &b, nil
- }
- }
-
- if b.Path != nil {
- if b.Path.MatchString(r.URL.Path) && s.checkRemoteAddress(b, addr) {
- return cr("bot/"+b.Name, b.Action), &b, nil
- }
- }
-
- if b.Ranger != nil {
- if s.checkRemoteAddress(b, addr) {
- return cr("bot/"+b.Name, b.Action), &b, nil
- }
+ match, err := b.Rules.Check(r)
+ if err != nil {
+ return decaymap.Zilch[policy.CheckResult](), nil, fmt.Errorf("can't run check %s: %w", b.Name, err)
}
- if len(b.Headers) > 0 {
- if s.checkHeaders(b, r.Header) {
- return cr("bot/"+b.Name, b.Action), &b, nil
- }
+ if match {
+ return cr("bot/"+b.Name, b.Action), &b, nil
}
}
@@ -565,40 +552,6 @@ func (s *Server) check(r *http.Request) (CheckResult, *policy.Bot, error) {
}, nil
}
-func (s *Server) checkRemoteAddress(b policy.Bot, addr net.IP) bool {
- if b.Ranger == nil {
- return true
- }
-
- ok, err := b.Ranger.Contains(addr)
- if err != nil {
- log.Panicf("[unexpected] something very funky is going on, %q does not have a calculable network number: %v", addr.String(), err)
- }
-
- return ok
-}
-
-func (s *Server) checkHeaders(b policy.Bot, header http.Header) bool {
- if len(b.Headers) == 0 {
- return true
- }
-
- for name, expr := range b.Headers {
- values := header.Values(name)
- if values == nil {
- return false
- }
-
- for _, value := range values {
- if !expr.MatchString(value) {
- return false
- }
- }
- }
-
- return true
-}
-
func (s *Server) CleanupDecayMap() {
s.DNSBLCache.Cleanup()
s.OGTags.Cleanup()