Compare commits

..

5 Commits

Author SHA1 Message Date
Xe Iaso 80a902229c fix(test): pki folder location
Signed-off-by: Xe Iaso <me@xeiaso.net>
2025-12-28 23:53:07 -05:00
Xe Iaso 158698827d fix(test): does this other thing work lol
Signed-off-by: Xe Iaso <me@xeiaso.net>
2025-12-28 23:41:51 -05:00
Xe Iaso 8e80c9c833 fix(test): does this work lol
Signed-off-by: Xe Iaso <me@xeiaso.net>
2025-12-28 23:38:19 -05:00
Jason Cameron b8d64093d3 Merge branch 'main' into Xe/fix-nginx-gha
Signed-off-by: Jason Cameron <git@jasoncameron.dev>
2025-12-28 22:19:28 -05:00
Xe Iaso 3990e73134 test(nginx): fix tests to work in GHA
Closes: #1371
Signed-off-by: Xe Iaso <me@xeiaso.net>
2025-12-28 22:12:34 -05:00
14 changed files with 420 additions and 706 deletions
-5
View File
@@ -18,8 +18,3 @@ clampip
pseudoprofound
reimagining
iocaine
admins
fout
iplist
NArg
blocklists
+4 -8
View File
@@ -87,14 +87,10 @@
^docs/docs/user/known-instances.md$
^docs/manifest/.*$
^docs/static/\.nojekyll$
^internal/glob/glob_test.go$
^internal/honeypot/naive/affirmations\.txt$
^internal/honeypot/naive/spintext\.txt$
^internal/honeypot/naive/titles\.txt$
^lib/config/testdata/bad/unparseable\.json$
^lib/localization/.*_test.go$
^lib/localization/locales/.*\.json$
^lib/policy/config/testdata/bad/unparseable\.json$
^test/.*$
^internal/glob/glob_test.go$
ignore$
robots.txt
^lib/localization/locales/.*\.json$
^lib/localization/.*_test.go$
^test/.*$
+11 -8
View File
@@ -73,7 +73,6 @@ Cromite
crt
Cscript
daemonizing
databento
dayjob
DDOS
Debian
@@ -133,7 +132,6 @@ GHSA
Ghz
gipc
gitea
GLM
godotenv
goland
gomod
@@ -234,7 +232,6 @@ nepeat
netsurf
nginx
nicksnyder
nikandfor
nobots
NONINFRINGEMENT
nosleep
@@ -251,7 +248,6 @@ opengraph
openrc
oswald
pag
pagegen
palemoon
Pangu
parseable
@@ -288,7 +284,6 @@ redirectscheme
refactors
remoteip
reputational
Rhul
risc
ruleset
runlevels
@@ -308,7 +303,6 @@ Seo
setsebool
shellcheck
shirou
shoneypot
shopt
Sidetrade
simprint
@@ -317,7 +311,6 @@ sls
sni
snipster
Spambot
spammer
sparkline
spyderbot
srv
@@ -361,6 +354,7 @@ valkey
Varis
Velen
vendored
verify
vhosts
vkbot
VKE
@@ -392,7 +386,6 @@ XNG
XOB
XOriginal
XReal
Y'shtola
yae
YAMLTo
Yda
@@ -404,3 +397,13 @@ Zenos
zizmor
zombocom
zos
GLM
iocaine
nikandfor
pagegen
pseudoprofound
reimagining
Rhul
shoneypot
spammer
Y'shtola
-3
View File
@@ -20,9 +20,6 @@ Anubis is brought to you by sponsors and donors like:
<a href="https://www.raptorcs.com/content/base/products.html">
<img src="./docs/static/img/sponsors/raptor-computing-logo.webp" alt="Raptor Computing Systems" height=64 />
</a>
<a href="https://databento.com/?utm_source=anubis&utm_medium=sponsor&utm_campaign=anubis">
<img src="./docs/static/img/sponsors/databento-logo.webp" alt="Databento" />
</a>
### Gold Tier
+1 -2
View File
@@ -3,6 +3,5 @@
- name: qualys-ssl-labs
action: ALLOW
remote_addresses:
- 69.67.183.0/24
- 64.41.200.0/24
- 2600:C02:1020:4202::/64
- 2602:fdaa:c6:2::/64
-1
View File
@@ -11,7 +11,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
- Add iplist2rule tool that lets admins turn an IP address blocklist into an Anubis ruleset.
- Add Polish locale ([#1292](https://github.com/TecharoHQ/anubis/pull/1309))
<!-- This changes the project to: -->
-50
View File
@@ -1,50 +0,0 @@
---
title: iplist2rule CLI tool
---
The `iplist2rule` tool converts IP blocklists into Anubis challenge policies. It reads common IP block list formats and generates the appropriate Anubis policy file for IP address filtering.
## Installation
Install directly with Go
```bash
go install github.com/TecharoHQ/anubis/utils/cmd/iplist2rule@latest
```
## Usage
Basic conversion from URL:
```bash
iplist2rule https://raw.githubusercontent.com/7c/torfilter/refs/heads/main/lists/txt/torfilter-1m-flat.txt filter-tor.yaml
```
Explicitly allow every IP address on a list:
```bash
iplist2rule --action ALLOW https://raw.githubusercontent.com/7c/torfilter/refs/heads/main/lists/txt/torfilter-1m-flat.txt filter-tor.yaml
```
Add weight to requests matching IP addresses on a list:
```bash
iplist2rule --action WEIGH --weight 20 https://raw.githubusercontent.com/7c/torfilter/refs/heads/main/lists/txt/torfilter-1m-flat.txt filter-tor.yaml
```
## Options
| Flag | Description | Default |
| :------------ | :----------------------------------------------------------------------------------------------- | :-------------------------------- |
| `--action` | The Anubis action to take for the IP address in question, must be in ALL CAPS. | `DENY` (forbids traffic) |
| `--rule-name` | The name for the generated Anubis rule, should be in kebab-case. | (not set, inferred from filename) |
| `--weight` | When `--action=WEIGH`, how many weight points should be added or removed from matching requests? | 0 (not set) |
## Using the Generated Policy
Save the output and import it in your main policy file:
```yaml
bots:
- import: "./filter-tor.yaml"
```
+4 -7
View File
@@ -12,7 +12,6 @@ Install directly with Go:
```bash
go install github.com/TecharoHQ/anubis/cmd/robots2policy@latest
```
## Usage
Basic conversion from URL:
@@ -36,8 +35,8 @@ robots2policy -input robots.txt -action DENY -format json
## Options
| Flag | Description | Default |
| --------------------- | ------------------------------------------------------------------ | ------------------- |
| `-input` | robots.txt file path or URL (use `-` for stdin) | _required_ |
|-----------------------|--------------------------------------------------------------------|---------------------|
| `-input` | robots.txt file path or URL (use `-` for stdin) | *required* |
| `-output` | Output file (use `-` for stdout) | stdout |
| `-format` | Output format: `yaml` or `json` | `yaml` |
| `-action` | Action for disallowed paths: `ALLOW`, `DENY`, `CHALLENGE`, `WEIGH` | `CHALLENGE` |
@@ -48,7 +47,6 @@ robots2policy -input robots.txt -action DENY -format json
## Example
Input robots.txt:
```txt
User-agent: *
Disallow: /admin/
@@ -59,7 +57,6 @@ Disallow: /
```
Generated policy:
```yaml
- name: robots-txt-policy-disallow-1
action: CHALLENGE
@@ -80,8 +77,8 @@ Generated policy:
Save the output and import it in your main policy file:
```yaml
bots:
- import: "./robots-policy.yaml"
import:
- path: "./robots-policy.yaml"
```
The tool handles wildcard patterns, user-agent specific rules, and blacklisted bots automatically.
-3
View File
@@ -29,9 +29,6 @@ Anubis is brought to you by sponsors and donors like:
height="64"
/>
</a>
<a href="https://databento.com/?utm_source=anubis&utm_medium=sponsor&utm_campaign=anubis">
<img src="/img/sponsors/databento-logo.webp" alt="Databento" />
</a>
### Gold Tier
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

+1 -22
View File
@@ -81,28 +81,7 @@ func (ls *LocalizationService) GetLocalizerFromRequest(r *http.Request) *i18n.Lo
return i18n.NewLocalizer(bundle, "en")
}
acceptLanguage := r.Header.Get("Accept-Language")
// Parse Accept-Language header to properly handle quality factors
// The language.ParseAcceptLanguage function returns tags sorted by quality
tags, _, err := language.ParseAcceptLanguage(acceptLanguage)
if err != nil || len(tags) == 0 {
return i18n.NewLocalizer(ls.bundle, "en")
}
// Convert parsed tags to strings for the localizer
// We include both the full tag and base language to ensure proper matching
langs := make([]string, 0, len(tags)*2+1)
for _, tag := range tags {
langs = append(langs, tag.String())
// Also add base language (e.g., "en" for "en-GB") to help matching
base, _ := tag.Base()
if base.String() != tag.String() {
langs = append(langs, base.String())
}
}
langs = append(langs, "en") // Always include English as fallback
return i18n.NewLocalizer(ls.bundle, langs...)
return i18n.NewLocalizer(ls.bundle, acceptLanguage, "en")
}
// SimpleLocalizer wraps i18n.Localizer with a more convenient API
-38
View File
@@ -3,7 +3,6 @@ package localization
import (
"encoding/json"
"fmt"
"net/http/httptest"
"sort"
"testing"
@@ -139,40 +138,3 @@ func TestComprehensiveTranslations(t *testing.T) {
})
}
}
func TestAcceptLanguageQualityFactors(t *testing.T) {
service := NewLocalizationService()
testCases := []struct {
name string
acceptLanguage string
expectedLang string
}{
{"simple_en", "en", "en"},
{"simple_de", "de", "de"},
{"en_GB_with_lower_priority_de", "en-GB,de-DE;q=0.5", "en"},
{"en_GB_only", "en-GB", "en"},
{"de_with_lower_priority_en", "de,en;q=0.5", "de"},
{"de_DE_with_lower_priority_en", "de-DE,en;q=0.5", "de"},
{"fr_with_lower_priority_de", "fr,de;q=0.5", "fr"},
{"zh_CN_regional", "zh-CN", "zh-CN"},
{"zh_TW_regional", "zh-TW", "zh-TW"},
{"pt_BR_regional", "pt-BR", "pt-BR"},
{"complex_header", "fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7,de;q=0.5", "fr"},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
req := httptest.NewRequest("GET", "/", nil)
req.Header.Set("Accept-Language", tc.acceptLanguage)
localizer := service.GetLocalizerFromRequest(req)
sl := &SimpleLocalizer{Localizer: localizer}
gotLang := sl.GetLang()
if gotLang != tc.expectedLang {
t.Errorf("Accept-Language %q: expected %s, got %s", tc.acceptLanguage, tc.expectedLang, gotLang)
}
})
}
}
-57
View File
@@ -1,57 +0,0 @@
package main
import (
"bufio"
"fmt"
"io"
"net/http"
"net/netip"
"strings"
)
// FetchBlocklist reads the blocklist over HTTP and returns every non-commented
// line parsed as an IP address in CIDR notation. IPv4 addresses are returned as
// /32, IPv6 addresses as /128.
//
// This function was generated with GLM 4.7.
func FetchBlocklist(url string) ([]string, error) {
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("HTTP request failed with status: %s", resp.Status)
}
var lines []string
scanner := bufio.NewScanner(resp.Body)
for scanner.Scan() {
line := scanner.Text()
// Skip empty lines and comments (lines starting with #)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
addr, err := netip.ParseAddr(line)
if err != nil {
// Skip lines that aren't valid IP addresses
continue
}
var cidr string
if addr.Is4() {
cidr = fmt.Sprintf("%s/32", addr.String())
} else {
cidr = fmt.Sprintf("%s/128", addr.String())
}
lines = append(lines, cidr)
}
if err := scanner.Err(); err != nil && err != io.EOF {
return nil, err
}
return lines, nil
}
-103
View File
@@ -1,103 +0,0 @@
package main
import (
"flag"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"time"
"github.com/TecharoHQ/anubis/lib/config"
"github.com/facebookgo/flagenv"
"sigs.k8s.io/yaml"
)
type Rule struct {
Name string `yaml:"name" json:"name"`
Action config.Rule `yaml:"action" json:"action"`
RemoteAddr []string `json:"remote_addresses,omitempty" yaml:"remote_addresses,omitempty"`
Weight *config.Weight `json:"weight,omitempty" yaml:"weight,omitempty"`
}
func init() {
flag.Usage = func() {
fmt.Printf(`Usage of %[1]s:
%[1]s [flags] <blocklist-url> <filename>
Grabs the contents of the blocklist, converts it to an Anubis ruleset, and writes it to filename.
Flags:
`, filepath.Base(os.Args[0]))
flag.PrintDefaults()
}
}
var (
action = flag.String("action", "DENY", "Anubis action to take (ALLOW / DENY / WEIGH)")
manualRuleName = flag.String("rule-name", "", "If set, prefer this name over inferring from filename")
weight = flag.Int("weight", 0, "If set to any number, add/subtract this many weight points when --action=WEIGH")
)
func main() {
flagenv.Parse()
flag.Parse()
if flag.NArg() != 2 {
flag.Usage()
os.Exit(2)
}
blocklistURL := flag.Arg(0)
foutName := flag.Arg(1)
ruleName := strings.TrimSuffix(foutName, filepath.Ext(foutName))
if *manualRuleName != "" {
ruleName = *manualRuleName
}
ruleAction := config.Rule(*action)
if err := ruleAction.Valid(); err != nil {
log.Fatalf("--action=%q is invalid: %v", *action, err)
}
result := &Rule{
Name: ruleName,
Action: ruleAction,
}
if *weight != 0 {
if ruleAction != config.RuleWeigh {
log.Fatalf("used --weight=%d but --action=%s", *weight, *action)
}
result.Weight = &config.Weight{
Adjust: *weight,
}
}
ips, err := FetchBlocklist(blocklistURL)
if err != nil {
log.Fatalf("can't fetch blocklist %s: %v", blocklistURL, err)
}
result.RemoteAddr = ips
fout, err := os.Create(foutName)
if err != nil {
log.Fatalf("can't create output file %q: %v", foutName, err)
}
defer fout.Close()
fmt.Fprintf(fout, "# Generated by %s on %s from %s\n\n", filepath.Base(os.Args[0]), time.Now().Format(time.RFC3339), blocklistURL)
data, err := yaml.Marshal([]*Rule{result})
if err != nil {
log.Fatalf("can't marshal yaml")
}
fout.Write(data)
}