Weighs the soul of incoming HTTP requests to stop AI crawlers

fix(anubis): improve challenge handling and error reporting (#645)

authored by

Jason Cameron and committed by
GitHub
bbdee34f 6e2eeb9e

+16 -10
+6 -6
lib/anubis.go
··· 288 288 return 289 289 } 290 290 lg = lg.With("check_result", cr) 291 - challenge := s.challengeFor(r, rule.Challenge.Difficulty) 291 + chal := s.challengeFor(r, rule.Challenge.Difficulty) 292 292 293 - s.SetCookie(w, anubis.TestCookieName, challenge, "/") 293 + s.SetCookie(w, anubis.TestCookieName, chal, "/") 294 294 295 295 err = encoder.Encode(struct { 296 296 Rules *config.ChallengeRules `json:"rules"` 297 297 Challenge string `json:"challenge"` 298 298 }{ 299 - Challenge: challenge, 299 + Challenge: chal, 300 300 Rules: rule.Challenge, 301 301 }) 302 302 if err != nil { ··· 304 304 w.WriteHeader(http.StatusInternalServerError) 305 305 return 306 306 } 307 - lg.Debug("made challenge", "challenge", challenge, "rules", rule.Challenge, "cr", cr) 307 + lg.Debug("made challenge", "challenge", chal, "rules", rule.Challenge, "cr", cr) 308 308 challengesIssued.WithLabelValues("api").Inc() 309 309 } 310 310 ··· 317 317 cookiePath = strings.TrimSuffix(anubis.BasePrefix, "/") + "/" 318 318 } 319 319 320 - if _, err := r.Cookie(anubis.TestCookieName); err == http.ErrNoCookie { 320 + if _, err := r.Cookie(anubis.TestCookieName); errors.Is(err, http.ErrNoCookie) { 321 321 s.ClearCookie(w, s.cookieName, cookiePath) 322 322 s.ClearCookie(w, anubis.TestCookieName, "/") 323 323 lg.Warn("user has cookies disabled, this is not an anubis bug") ··· 365 365 challengeStr := s.challengeFor(r, rule.Challenge.Difficulty) 366 366 367 367 if err := impl.Validate(r, lg, rule, challengeStr); err != nil { 368 - failedValidations.WithLabelValues(string(rule.Challenge.Algorithm)).Inc() 368 + failedValidations.WithLabelValues(rule.Challenge.Algorithm).Inc() 369 369 var cerr *challenge.Error 370 370 s.ClearCookie(w, s.cookieName, cookiePath) 371 371 lg.Debug("challenge validate call failed", "err", err)
+4 -1
lib/config.go
··· 67 67 }(fin) 68 68 69 69 anubisPolicy, err := policy.ParseConfig(fin, fname, defaultDifficulty) 70 + if err != nil { 71 + return nil, fmt.Errorf("can't parse policy file %s: %w", fname, err) 72 + } 70 73 var validationErrs []error 71 74 72 75 for _, b := range anubisPolicy.Bots { ··· 154 157 // make-challenge is only used in tests. Only enable while version is devel 155 158 registerWithPrefix(anubis.APIPrefix+"make-challenge", http.HandlerFunc(result.MakeChallenge), "POST") 156 159 } 157 - 160 + 158 161 for _, implKind := range challenge.Methods() { 159 162 impl, _ := challenge.Get(implKind) 160 163 impl.Setup(mux)
+3 -3
test/go.mod
··· 12 12 13 13 require ( 14 14 cel.dev/expr v0.24.0 // indirect 15 - github.com/a-h/templ v0.3.865 // indirect 15 + github.com/a-h/templ v0.3.898 // indirect 16 16 github.com/antlr4-go/antlr/v4 v4.13.1 // indirect 17 17 github.com/beorn7/perks v1.0.1 // indirect 18 18 github.com/cespare/xxhash/v2 v2.3.0 // indirect ··· 32 32 github.com/stoewer/go-strcase v1.3.0 // indirect 33 33 github.com/yl2chen/cidranger v1.0.2 // indirect 34 34 golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect 35 - golang.org/x/net v0.40.0 // indirect 35 + golang.org/x/net v0.41.0 // indirect 36 36 golang.org/x/sys v0.33.0 // indirect 37 - golang.org/x/text v0.25.0 // indirect 37 + golang.org/x/text v0.26.0 // indirect 38 38 google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect 39 39 google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect 40 40 google.golang.org/protobuf v1.36.6 // indirect
+3
test/go.sum
··· 2 2 cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= 3 3 github.com/a-h/templ v0.3.865 h1:nYn5EWm9EiXaDgWcMQaKiKvrydqgxDUtT1+4zU2C43A= 4 4 github.com/a-h/templ v0.3.865/go.mod h1:oLBbZVQ6//Q6zpvSMPTuBK0F3qOtBdFBcGRspcT+VNQ= 5 + github.com/a-h/templ v0.3.898/go.mod h1:oLBbZVQ6//Q6zpvSMPTuBK0F3qOtBdFBcGRspcT+VNQ= 5 6 github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= 6 7 github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= 7 8 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= ··· 73 74 golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= 74 75 golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= 75 76 golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= 77 + golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= 76 78 golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= 77 79 golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 78 80 golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= 79 81 golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= 82 + golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= 80 83 google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= 81 84 google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= 82 85 google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34=