Skip to content

Commit 7aa732c

Browse files
authored
fix(config): actually load threshold config (#696)
* fix(config): actually load threshold config Signed-off-by: Xe Iaso <[email protected]> * chore: spelling Signed-off-by: Xe Iaso <[email protected]> * test(lib): fix test failures Signed-off-by: Xe Iaso <[email protected]> --------- Signed-off-by: Xe Iaso <[email protected]>
1 parent 226cf36 commit 7aa732c

File tree

12 files changed

+201
-30
lines changed

12 files changed

+201
-30
lines changed

.github/actions/spelling/excludes.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@
8383
^\Q.github/FUNDING.yml\E$
8484
^\Q.github/workflows/spelling.yml\E$
8585
^data/crawlers/
86+
^docs/blog/tags\.yml$
8687
^docs/manifest/.*$
8788
^docs/static/\.nojekyll$
89+
^lib/policy/config/testdata/bad/unparseable\.json$
8890
ignore$
8991
robots.txt

.github/actions/spelling/expect.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ chall
4444
challengemozilla
4545
checkpath
4646
checkresult
47-
chen
4847
chibi
4948
cidranger
5049
ckie
@@ -61,7 +60,6 @@ DDOS
6160
Debian
6261
debrpm
6362
decaymap
64-
decompiling
6563
Diffbot
6664
discordapp
6765
discordbot
@@ -300,6 +298,7 @@ xess
300298
xff
301299
XForwarded
302300
XNG
301+
XOB
303302
XReal
304303
yae
305304
YAMLTo

lib/anubis_test.go

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,16 @@ func init() {
2424
internal.InitSlog("debug")
2525
}
2626

27-
func loadPolicies(t *testing.T, fname string) *policy.ParsedConfig {
27+
func loadPolicies(t *testing.T, fname string, difficulty int) *policy.ParsedConfig {
2828
t.Helper()
2929

3030
ctx := thothmock.WithMockThoth(t)
3131

32-
anubisPolicy, err := LoadPoliciesOrDefault(ctx, fname, anubis.DefaultDifficulty)
32+
if fname == "" {
33+
fname = "./testdata/test_config.yaml"
34+
}
35+
36+
anubisPolicy, err := LoadPoliciesOrDefault(ctx, fname, difficulty)
3337
if err != nil {
3438
t.Fatal(err)
3539
}
@@ -176,8 +180,7 @@ func TestLoadPolicies(t *testing.T) {
176180

177181
// Regression test for CVE-2025-24369
178182
func TestCVE2025_24369(t *testing.T) {
179-
pol := loadPolicies(t, "")
180-
pol.DefaultDifficulty = 4
183+
pol := loadPolicies(t, "", anubis.DefaultDifficulty)
181184

182185
srv := spawnAnubis(t, Options{
183186
Next: http.NewServeMux(),
@@ -200,8 +203,7 @@ func TestCVE2025_24369(t *testing.T) {
200203
}
201204

202205
func TestCookieCustomExpiration(t *testing.T) {
203-
pol := loadPolicies(t, "")
204-
pol.DefaultDifficulty = 0
206+
pol := loadPolicies(t, "", 0)
205207
ckieExpiration := 10 * time.Minute
206208

207209
srv := spawnAnubis(t, Options{
@@ -250,8 +252,7 @@ func TestCookieCustomExpiration(t *testing.T) {
250252
}
251253

252254
func TestCookieSettings(t *testing.T) {
253-
pol := loadPolicies(t, "")
254-
pol.DefaultDifficulty = 0
255+
pol := loadPolicies(t, "", 0)
255256

256257
srv := spawnAnubis(t, Options{
257258
Next: http.NewServeMux(),
@@ -316,10 +317,7 @@ func TestCheckDefaultDifficultyMatchesPolicy(t *testing.T) {
316317

317318
for i := 1; i < 10; i++ {
318319
t.Run(fmt.Sprint(i), func(t *testing.T) {
319-
anubisPolicy, err := LoadPoliciesOrDefault(t.Context(), "", i)
320-
if err != nil {
321-
t.Fatal(err)
322-
}
320+
anubisPolicy := loadPolicies(t, "", i)
323321

324322
s, err := New(Options{
325323
Next: h,
@@ -337,11 +335,13 @@ func TestCheckDefaultDifficultyMatchesPolicy(t *testing.T) {
337335

338336
req.Header.Add("X-Real-Ip", "127.0.0.1")
339337

340-
_, bot, err := s.check(req)
338+
cr, bot, err := s.check(req)
341339
if err != nil {
342340
t.Fatal(err)
343341
}
344342

343+
t.Log(cr.Name)
344+
345345
if bot.Challenge.Difficulty != i {
346346
t.Errorf("Challenge.Difficulty is wrong, wanted %d, got: %d", i, bot.Challenge.Difficulty)
347347
}
@@ -389,8 +389,7 @@ func TestBasePrefix(t *testing.T) {
389389
// Reset the global BasePrefix before each test
390390
anubis.BasePrefix = ""
391391

392-
pol := loadPolicies(t, "")
393-
pol.DefaultDifficulty = 4
392+
pol := loadPolicies(t, "", 4)
394393

395394
srv := spawnAnubis(t, Options{
396395
Next: h,
@@ -518,8 +517,7 @@ func TestCustomStatusCodes(t *testing.T) {
518517
"DENY": 403,
519518
}
520519

521-
pol := loadPolicies(t, "./testdata/aggressive_403.yaml")
522-
pol.DefaultDifficulty = 4
520+
pol := loadPolicies(t, "./testdata/aggressive_403.yaml", 4)
523521

524522
srv := spawnAnubis(t, Options{
525523
Next: h,
@@ -553,7 +551,7 @@ func TestCustomStatusCodes(t *testing.T) {
553551
func TestCloudflareWorkersRule(t *testing.T) {
554552
for _, variant := range []string{"cel", "header"} {
555553
t.Run(variant, func(t *testing.T) {
556-
pol := loadPolicies(t, "./testdata/cloudflare-workers-"+variant+".yaml")
554+
pol := loadPolicies(t, "./testdata/cloudflare-workers-"+variant+".yaml", 0)
557555

558556
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
559557
fmt.Fprintln(w, "OK")
@@ -609,8 +607,7 @@ func TestCloudflareWorkersRule(t *testing.T) {
609607
}
610608

611609
func TestRuleChange(t *testing.T) {
612-
pol := loadPolicies(t, "testdata/rule_change.yaml")
613-
pol.DefaultDifficulty = 0
610+
pol := loadPolicies(t, "testdata/rule_change.yaml", 0)
614611
ckieExpiration := 10 * time.Minute
615612

616613
srv := spawnAnubis(t, Options{

lib/config_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func TestBadConfigs(t *testing.T) {
2626
for _, st := range finfos {
2727
st := st
2828
t.Run(st.Name(), func(t *testing.T) {
29-
if _, err := LoadPoliciesOrDefault(t.Context(), filepath.Join("policy", "config", "testdata", "good", st.Name()), anubis.DefaultDifficulty); err == nil {
29+
if _, err := LoadPoliciesOrDefault(t.Context(), filepath.Join("policy", "config", "testdata", "bad", st.Name()), anubis.DefaultDifficulty); err == nil {
3030
t.Fatal(err)
3131
} else {
3232
t.Log(err)

lib/policy/config/asn_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package config
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"testing"
7+
)
8+
9+
func TestASNsValid(t *testing.T) {
10+
for _, tt := range []struct {
11+
name string
12+
input *ASNs
13+
err error
14+
}{
15+
{
16+
name: "basic valid",
17+
input: &ASNs{
18+
Match: []uint32{13335}, // Cloudflare
19+
},
20+
},
21+
{
22+
name: "private ASN",
23+
input: &ASNs{
24+
Match: []uint32{64513, 4206942069}, // 16 and 32 bit private ASN
25+
},
26+
err: ErrPrivateASN,
27+
},
28+
} {
29+
t.Run(tt.name, func(t *testing.T) {
30+
if err := tt.input.Valid(); !errors.Is(err, tt.err) {
31+
t.Logf("want: %v", tt.err)
32+
t.Logf("got: %v", err)
33+
t.Error("got wrong validation error")
34+
}
35+
})
36+
}
37+
}
38+
39+
func TestIsPrivateASN(t *testing.T) {
40+
for _, tt := range []struct {
41+
input uint32
42+
output bool
43+
}{
44+
{13335, false}, // Cloudflare
45+
{64513, true}, // 16 bit private ASN
46+
{4206942069, true}, // 32 bit private ASN
47+
} {
48+
t.Run(fmt.Sprint(tt.input, "->", tt.output), func(t *testing.T) {
49+
result := isPrivateASN(tt.input)
50+
if result != tt.output {
51+
t.Errorf("wanted isPrivateASN(%d) == %v, got: %v", tt.input, tt.output, result)
52+
}
53+
})
54+
}
55+
}

lib/policy/config/config.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ type fileConfig struct {
326326
Bots []BotOrImport `json:"bots"`
327327
DNSBL bool `json:"dnsbl"`
328328
StatusCodes StatusCodes `json:"status_codes"`
329-
Thresholds []Threshold `json:"threshold"`
329+
Thresholds []Threshold `json:"thresholds"`
330330
}
331331

332332
func (c *fileConfig) Valid() error {
@@ -346,10 +346,6 @@ func (c *fileConfig) Valid() error {
346346
errs = append(errs, err)
347347
}
348348

349-
if len(c.Thresholds) == 0 {
350-
errs = append(errs, ErrNoThresholdRulesDefined)
351-
}
352-
353349
for i, t := range c.Thresholds {
354350
if err := t.Valid(); err != nil {
355351
errs = append(errs, fmt.Errorf("threshold %d: %w", i, err))
@@ -369,7 +365,6 @@ func Load(fin io.Reader, fname string) (*Config, error) {
369365
Challenge: http.StatusOK,
370366
Deny: http.StatusOK,
371367
},
372-
Thresholds: DefaultThresholds,
373368
}
374369

375370
if err := yaml.NewYAMLToJSONDecoder(fin).Decode(&c); err != nil {
@@ -407,6 +402,10 @@ func Load(fin io.Reader, fname string) (*Config, error) {
407402
}
408403
}
409404

405+
if len(c.Thresholds) == 0 {
406+
c.Thresholds = DefaultThresholds
407+
}
408+
410409
for _, t := range c.Thresholds {
411410
if err := t.Valid(); err != nil {
412411
validationErrs = append(validationErrs, err)

lib/policy/config/geoip.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
)
99

1010
var (
11-
countryCodeRegexp = regexp.MustCompile(`^\w{2}$`)
11+
countryCodeRegexp = regexp.MustCompile(`^[a-zA-Z]{2}$`)
1212

1313
ErrNotCountryCode = errors.New("config.Bot: invalid country code")
1414
)

lib/policy/config/geoip_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package config
2+
3+
import (
4+
"errors"
5+
"testing"
6+
)
7+
8+
func TestGeoIPValid(t *testing.T) {
9+
for _, tt := range []struct {
10+
name string
11+
input *GeoIP
12+
err error
13+
}{
14+
{
15+
name: "basic valid",
16+
input: &GeoIP{
17+
Countries: []string{"CA"},
18+
},
19+
},
20+
{
21+
name: "invalid country",
22+
input: &GeoIP{
23+
Countries: []string{"XOB"},
24+
},
25+
err: ErrNotCountryCode,
26+
},
27+
} {
28+
t.Run(tt.name, func(t *testing.T) {
29+
if err := tt.input.Valid(); !errors.Is(err, tt.err) {
30+
t.Logf("want: %v", tt.err)
31+
t.Logf("got: %v", err)
32+
t.Error("got wrong validation error")
33+
}
34+
})
35+
}
36+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
bots:
2+
- name: simple-weight-adjust
3+
action: WEIGH
4+
user_agent_regex: Mozilla
5+
weight:
6+
adjust: 5
7+
8+
thresholds:
9+
- name: extreme-suspicion
10+
expression: "true"
11+
action: WEIGH
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
bots:
2+
- name: simple-weight-adjust
3+
action: WEIGH
4+
user_agent_regex: Mozilla
5+
weight:
6+
adjust: 5
7+
8+
thresholds:
9+
- name: extreme-suspicion
10+
expression: "true"
11+
action: WEIGH
12+
challenge:
13+
algorithm: fast
14+
difficulty: 4
15+
report_as: 4

0 commit comments

Comments
 (0)