mirror of
https://github.com/TecharoHQ/anubis.git
synced 2026-04-05 16:28:17 +00:00
Compare commits
12 Commits
v1.24.0-pr
...
Xe/logrota
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad680d8a48 | ||
|
|
7a9590efd8 | ||
|
|
524efe3126 | ||
|
|
13adb0ca35 | ||
|
|
19215664d5 | ||
|
|
96232ec191 | ||
|
|
96d3c70f4c | ||
|
|
ad4a3ab202 | ||
|
|
0b0e10a284 | ||
|
|
b654d4d709 | ||
|
|
d55979250f | ||
|
|
8912050882 |
2
.github/actions/spelling/allow.txt
vendored
2
.github/actions/spelling/allow.txt
vendored
@@ -10,5 +10,3 @@ ABee
|
||||
tencent
|
||||
maintnotifications
|
||||
azurediamond
|
||||
cooldown
|
||||
verifyfcrdns
|
||||
|
||||
791
.github/actions/spelling/expect.txt
vendored
791
.github/actions/spelling/expect.txt
vendored
@@ -1,400 +1,391 @@
|
||||
|
||||
acs
|
||||
Actorified
|
||||
actorifiedstore
|
||||
actorify
|
||||
Aibrew
|
||||
alibaba
|
||||
alrest
|
||||
amazonbot
|
||||
anthro
|
||||
anubis
|
||||
anubistest
|
||||
apnic
|
||||
APNICRANDNETAU
|
||||
Applebot
|
||||
archlinux
|
||||
arpa
|
||||
asnc
|
||||
asnchecker
|
||||
asns
|
||||
aspirational
|
||||
atuin
|
||||
azuretools
|
||||
badregexes
|
||||
bbolt
|
||||
bdba
|
||||
berr
|
||||
bezier
|
||||
bingbot
|
||||
Bitcoin
|
||||
bitrate
|
||||
Bluesky
|
||||
blueskybot
|
||||
boi
|
||||
Bokm
|
||||
botnet
|
||||
botstopper
|
||||
BPort
|
||||
Brightbot
|
||||
broked
|
||||
buildah
|
||||
byteslice
|
||||
Bytespider
|
||||
cachebuster
|
||||
cachediptoasn
|
||||
Caddyfile
|
||||
caninetools
|
||||
Cardyb
|
||||
celchecker
|
||||
celphase
|
||||
cerr
|
||||
certresolver
|
||||
cespare
|
||||
CGNAT
|
||||
cgr
|
||||
chainguard
|
||||
chall
|
||||
challengemozilla
|
||||
challengetest
|
||||
checkpath
|
||||
checkresult
|
||||
chibi
|
||||
cidranger
|
||||
ckie
|
||||
cloudflare
|
||||
Codespaces
|
||||
confd
|
||||
connnection
|
||||
containerbuild
|
||||
containerregistry
|
||||
coreutils
|
||||
Cotoyogi
|
||||
Cromite
|
||||
crt
|
||||
Cscript
|
||||
daemonizing
|
||||
dayjob
|
||||
DDOS
|
||||
Debian
|
||||
debrpm
|
||||
decaymap
|
||||
devcontainers
|
||||
Diffbot
|
||||
discordapp
|
||||
discordbot
|
||||
distros
|
||||
dnf
|
||||
dnsbl
|
||||
dnserr
|
||||
DNSTTL
|
||||
domainhere
|
||||
dracula
|
||||
dronebl
|
||||
droneblresponse
|
||||
dropin
|
||||
dsilence
|
||||
duckduckbot
|
||||
eerror
|
||||
ellenjoe
|
||||
emacs
|
||||
enbyware
|
||||
etld
|
||||
everyones
|
||||
evilbot
|
||||
evilsite
|
||||
expressionorlist
|
||||
externalagent
|
||||
externalfetcher
|
||||
extldflags
|
||||
facebookgo
|
||||
Factset
|
||||
fahedouch
|
||||
fastcgi
|
||||
FCr
|
||||
fcrdns
|
||||
fediverse
|
||||
ffprobe
|
||||
financials
|
||||
finfos
|
||||
Firecrawl
|
||||
flagenv
|
||||
Fordola
|
||||
forgejo
|
||||
forwardauth
|
||||
fsys
|
||||
fullchain
|
||||
gaissmai
|
||||
Galvus
|
||||
geoip
|
||||
geoipchecker
|
||||
gha
|
||||
GHSA
|
||||
Ghz
|
||||
gipc
|
||||
gitea
|
||||
godotenv
|
||||
goland
|
||||
gomod
|
||||
goodbot
|
||||
googlebot
|
||||
gopsutil
|
||||
govulncheck
|
||||
goyaml
|
||||
GPG
|
||||
GPT
|
||||
gptbot
|
||||
Graphene
|
||||
grpcprom
|
||||
grw
|
||||
gzw
|
||||
Hashcash
|
||||
hashrate
|
||||
headermap
|
||||
healthcheck
|
||||
healthz
|
||||
hec
|
||||
helpdesk
|
||||
Hetzner
|
||||
hmc
|
||||
homelab
|
||||
hostable
|
||||
htmlc
|
||||
htmx
|
||||
httpdebug
|
||||
huawei
|
||||
hypertext
|
||||
iaskspider
|
||||
iaso
|
||||
iat
|
||||
ifm
|
||||
Imagesift
|
||||
imgproxy
|
||||
impressum
|
||||
inbox
|
||||
ingressed
|
||||
inp
|
||||
internets
|
||||
IPTo
|
||||
iptoasn
|
||||
isp
|
||||
iss
|
||||
isset
|
||||
ivh
|
||||
Jenomis
|
||||
JGit
|
||||
jhjj
|
||||
joho
|
||||
journalctl
|
||||
jshelter
|
||||
JWTs
|
||||
kagi
|
||||
kagibot
|
||||
Keyfunc
|
||||
keypair
|
||||
KHTML
|
||||
kinda
|
||||
KUBECONFIG
|
||||
lcj
|
||||
ldflags
|
||||
letsencrypt
|
||||
Lexentale
|
||||
lfc
|
||||
lgbt
|
||||
licend
|
||||
licstart
|
||||
lightpanda
|
||||
limsa
|
||||
Linting
|
||||
listor
|
||||
LLU
|
||||
loadbalancer
|
||||
lol
|
||||
lominsa
|
||||
maintainership
|
||||
malware
|
||||
mcr
|
||||
memes
|
||||
metarefresh
|
||||
metrix
|
||||
mimi
|
||||
Minfilia
|
||||
mistralai
|
||||
mnt
|
||||
Mojeek
|
||||
mojeekbot
|
||||
mozilla
|
||||
myclient
|
||||
mymaster
|
||||
mypass
|
||||
myuser
|
||||
nbf
|
||||
nepeat
|
||||
netsurf
|
||||
nginx
|
||||
nicksnyder
|
||||
nobots
|
||||
NONINFRINGEMENT
|
||||
nosleep
|
||||
nullglob
|
||||
oci
|
||||
OCOB
|
||||
ogtag
|
||||
oklch
|
||||
omgili
|
||||
omgilibot
|
||||
openai
|
||||
opendns
|
||||
opengraph
|
||||
openrc
|
||||
oswald
|
||||
pag
|
||||
palemoon
|
||||
Pangu
|
||||
parseable
|
||||
passthrough
|
||||
Patreon
|
||||
pgrep
|
||||
phrik
|
||||
pidfile
|
||||
pids
|
||||
pipefail
|
||||
pki
|
||||
podkova
|
||||
podman
|
||||
Postgre
|
||||
poststart
|
||||
prebaked
|
||||
privkey
|
||||
promauto
|
||||
promhttp
|
||||
proofofwork
|
||||
publicsuffix
|
||||
purejs
|
||||
pwcmd
|
||||
pwuser
|
||||
qualys
|
||||
qwant
|
||||
qwantbot
|
||||
rac
|
||||
rawler
|
||||
rcvar
|
||||
redhat
|
||||
redir
|
||||
redirectscheme
|
||||
refactors
|
||||
remoteip
|
||||
reputational
|
||||
risc
|
||||
ruleset
|
||||
runlevels
|
||||
RUnlock
|
||||
runtimedir
|
||||
runtimedirectory
|
||||
Ryzen
|
||||
sas
|
||||
sasl
|
||||
screenshots
|
||||
searchbot
|
||||
searx
|
||||
sebest
|
||||
secretplans
|
||||
Semrush
|
||||
Seo
|
||||
setsebool
|
||||
shellcheck
|
||||
shirou
|
||||
shopt
|
||||
Sidetrade
|
||||
simprint
|
||||
sitemap
|
||||
sls
|
||||
sni
|
||||
snipster
|
||||
Spambot
|
||||
sparkline
|
||||
spyderbot
|
||||
srv
|
||||
stackoverflow
|
||||
startprecmd
|
||||
stoppostcmd
|
||||
storetest
|
||||
subgrid
|
||||
subr
|
||||
subrequest
|
||||
SVCNAME
|
||||
tagline
|
||||
tarballs
|
||||
tarrif
|
||||
taviso
|
||||
tbn
|
||||
tbr
|
||||
techaro
|
||||
techarohq
|
||||
telegrambot
|
||||
templ
|
||||
templruntime
|
||||
testarea
|
||||
Thancred
|
||||
thoth
|
||||
thothmock
|
||||
Tik
|
||||
Timpibot
|
||||
TLog
|
||||
traefik
|
||||
trunc
|
||||
uberspace
|
||||
Unbreak
|
||||
unbreakdocker
|
||||
unifiedjs
|
||||
unmarshal
|
||||
unparseable
|
||||
uvx
|
||||
UXP
|
||||
valkey
|
||||
Varis
|
||||
Velen
|
||||
vendored
|
||||
verify
|
||||
vhosts
|
||||
vkbot
|
||||
VKE
|
||||
vnd
|
||||
VPS
|
||||
Vultr
|
||||
weblate
|
||||
webmaster
|
||||
webpage
|
||||
websecure
|
||||
websites
|
||||
Webzio
|
||||
whois
|
||||
wildbase
|
||||
withthothmock
|
||||
wolfbeast
|
||||
wordpress
|
||||
workaround
|
||||
workdir
|
||||
wpbot
|
||||
XCircle
|
||||
xeiaso
|
||||
xeserv
|
||||
xesite
|
||||
xess
|
||||
xff
|
||||
XForwarded
|
||||
XNG
|
||||
XOB
|
||||
XOriginal
|
||||
XReal
|
||||
yae
|
||||
YAMLTo
|
||||
Yda
|
||||
yeet
|
||||
yeetfile
|
||||
yourdomain
|
||||
yyz
|
||||
Zenos
|
||||
zizmor
|
||||
zombocom
|
||||
zos
|
||||
acs
|
||||
Actorified
|
||||
actorifiedstore
|
||||
actorify
|
||||
Aibrew
|
||||
alibaba
|
||||
alrest
|
||||
amazonbot
|
||||
anthro
|
||||
anubis
|
||||
anubistest
|
||||
apnic
|
||||
APNICRANDNETAU
|
||||
Applebot
|
||||
archlinux
|
||||
asnc
|
||||
asnchecker
|
||||
asns
|
||||
aspirational
|
||||
atuin
|
||||
azuretools
|
||||
badregexes
|
||||
bbolt
|
||||
bdba
|
||||
berr
|
||||
bezier
|
||||
bingbot
|
||||
Bitcoin
|
||||
bitrate
|
||||
Bluesky
|
||||
blueskybot
|
||||
boi
|
||||
Bokm
|
||||
botnet
|
||||
botstopper
|
||||
BPort
|
||||
Brightbot
|
||||
broked
|
||||
buildah
|
||||
byteslice
|
||||
Bytespider
|
||||
cachebuster
|
||||
cachediptoasn
|
||||
Caddyfile
|
||||
caninetools
|
||||
Cardyb
|
||||
celchecker
|
||||
celphase
|
||||
cerr
|
||||
certresolver
|
||||
cespare
|
||||
CGNAT
|
||||
cgr
|
||||
chainguard
|
||||
chall
|
||||
challengemozilla
|
||||
challengetest
|
||||
checkpath
|
||||
checkresult
|
||||
chibi
|
||||
cidranger
|
||||
ckie
|
||||
cloudflare
|
||||
Codespaces
|
||||
confd
|
||||
connnection
|
||||
containerbuild
|
||||
containerregistry
|
||||
coreutils
|
||||
Cotoyogi
|
||||
Cromite
|
||||
crt
|
||||
Cscript
|
||||
daemonizing
|
||||
dayjob
|
||||
DDOS
|
||||
Debian
|
||||
debrpm
|
||||
decaymap
|
||||
devcontainers
|
||||
Diffbot
|
||||
discordapp
|
||||
discordbot
|
||||
distros
|
||||
dnf
|
||||
dnsbl
|
||||
dnserr
|
||||
domainhere
|
||||
dracula
|
||||
dronebl
|
||||
droneblresponse
|
||||
dropin
|
||||
dsilence
|
||||
duckduckbot
|
||||
eerror
|
||||
ellenjoe
|
||||
emacs
|
||||
enbyware
|
||||
etld
|
||||
everyones
|
||||
evilbot
|
||||
evilsite
|
||||
expressionorlist
|
||||
externalagent
|
||||
externalfetcher
|
||||
extldflags
|
||||
facebookgo
|
||||
Factset
|
||||
fahedouch
|
||||
fastcgi
|
||||
fediverse
|
||||
ffprobe
|
||||
financials
|
||||
finfos
|
||||
Firecrawl
|
||||
flagenv
|
||||
Fordola
|
||||
forgejo
|
||||
forwardauth
|
||||
fsys
|
||||
fullchain
|
||||
gaissmai
|
||||
Galvus
|
||||
geoip
|
||||
geoipchecker
|
||||
gha
|
||||
GHSA
|
||||
Ghz
|
||||
gipc
|
||||
gitea
|
||||
godotenv
|
||||
goland
|
||||
gomod
|
||||
goodbot
|
||||
googlebot
|
||||
gopsutil
|
||||
govulncheck
|
||||
goyaml
|
||||
GPG
|
||||
GPT
|
||||
gptbot
|
||||
Graphene
|
||||
grpcprom
|
||||
grw
|
||||
gzw
|
||||
Hashcash
|
||||
hashrate
|
||||
headermap
|
||||
healthcheck
|
||||
healthz
|
||||
hec
|
||||
helpdesk
|
||||
Hetzner
|
||||
hmc
|
||||
homelab
|
||||
hostable
|
||||
htmlc
|
||||
htmx
|
||||
httpdebug
|
||||
Huawei
|
||||
huawei
|
||||
hypertext
|
||||
iaskspider
|
||||
iaso
|
||||
iat
|
||||
ifm
|
||||
Imagesift
|
||||
imgproxy
|
||||
impressum
|
||||
inbox
|
||||
ingressed
|
||||
inp
|
||||
internets
|
||||
IPTo
|
||||
iptoasn
|
||||
isp
|
||||
iss
|
||||
isset
|
||||
ivh
|
||||
Jenomis
|
||||
JGit
|
||||
jhjj
|
||||
joho
|
||||
journalctl
|
||||
jshelter
|
||||
JWTs
|
||||
kagi
|
||||
kagibot
|
||||
Keyfunc
|
||||
keypair
|
||||
KHTML
|
||||
kinda
|
||||
KUBECONFIG
|
||||
lcj
|
||||
ldflags
|
||||
letsencrypt
|
||||
Lexentale
|
||||
lfc
|
||||
lgbt
|
||||
licend
|
||||
licstart
|
||||
lightpanda
|
||||
limsa
|
||||
Linting
|
||||
listor
|
||||
LLU
|
||||
loadbalancer
|
||||
lol
|
||||
lominsa
|
||||
maintainership
|
||||
malware
|
||||
mcr
|
||||
memes
|
||||
metarefresh
|
||||
metrix
|
||||
mimi
|
||||
Minfilia
|
||||
mistralai
|
||||
mnt
|
||||
Mojeek
|
||||
mojeekbot
|
||||
mozilla
|
||||
myclient
|
||||
mymaster
|
||||
mypass
|
||||
myuser
|
||||
nbf
|
||||
nepeat
|
||||
netsurf
|
||||
nginx
|
||||
nicksnyder
|
||||
nobots
|
||||
NONINFRINGEMENT
|
||||
nosleep
|
||||
nullglob
|
||||
oci
|
||||
OCOB
|
||||
ogtag
|
||||
oklch
|
||||
omgili
|
||||
omgilibot
|
||||
openai
|
||||
opengraph
|
||||
openrc
|
||||
oswald
|
||||
pag
|
||||
palemoon
|
||||
Pangu
|
||||
parseable
|
||||
passthrough
|
||||
Patreon
|
||||
pgrep
|
||||
phrik
|
||||
pidfile
|
||||
pids
|
||||
pipefail
|
||||
pki
|
||||
podkova
|
||||
podman
|
||||
Postgre
|
||||
poststart
|
||||
prebaked
|
||||
privkey
|
||||
promauto
|
||||
promhttp
|
||||
proofofwork
|
||||
publicsuffix
|
||||
purejs
|
||||
pwcmd
|
||||
pwuser
|
||||
qualys
|
||||
qwant
|
||||
qwantbot
|
||||
rac
|
||||
rawler
|
||||
rcvar
|
||||
redhat
|
||||
redir
|
||||
redirectscheme
|
||||
refactors
|
||||
reputational
|
||||
risc
|
||||
ruleset
|
||||
runlevels
|
||||
RUnlock
|
||||
runtimedir
|
||||
runtimedirectory
|
||||
Ryzen
|
||||
sas
|
||||
sasl
|
||||
screenshots
|
||||
searchbot
|
||||
searx
|
||||
sebest
|
||||
secretplans
|
||||
Semrush
|
||||
Seo
|
||||
setsebool
|
||||
shellcheck
|
||||
shirou
|
||||
shopt
|
||||
Sidetrade
|
||||
simprint
|
||||
sitemap
|
||||
sls
|
||||
sni
|
||||
Spambot
|
||||
sparkline
|
||||
spyderbot
|
||||
srv
|
||||
stackoverflow
|
||||
startprecmd
|
||||
stoppostcmd
|
||||
storetest
|
||||
subgrid
|
||||
subr
|
||||
subrequest
|
||||
SVCNAME
|
||||
tagline
|
||||
tarballs
|
||||
tarrif
|
||||
taviso
|
||||
tbn
|
||||
tbr
|
||||
techaro
|
||||
techarohq
|
||||
templ
|
||||
templruntime
|
||||
testarea
|
||||
Thancred
|
||||
thoth
|
||||
thothmock
|
||||
Tik
|
||||
Timpibot
|
||||
TLog
|
||||
traefik
|
||||
trunc
|
||||
uberspace
|
||||
Unbreak
|
||||
unbreakdocker
|
||||
unifiedjs
|
||||
unmarshal
|
||||
unparseable
|
||||
uvx
|
||||
UXP
|
||||
valkey
|
||||
Varis
|
||||
Velen
|
||||
vendored
|
||||
vhosts
|
||||
VKE
|
||||
vnd
|
||||
VPS
|
||||
Vultr
|
||||
weblate
|
||||
webmaster
|
||||
webpage
|
||||
websecure
|
||||
websites
|
||||
Webzio
|
||||
whois
|
||||
wildbase
|
||||
withthothmock
|
||||
wolfbeast
|
||||
wordpress
|
||||
Workaround
|
||||
workaround
|
||||
workdir
|
||||
wpbot
|
||||
XCircle
|
||||
xeiaso
|
||||
xeserv
|
||||
xesite
|
||||
xess
|
||||
xff
|
||||
XForwarded
|
||||
XNG
|
||||
XOB
|
||||
XOriginal
|
||||
XReal
|
||||
yae
|
||||
YAMLTo
|
||||
Yda
|
||||
yeet
|
||||
yeetfile
|
||||
yourdomain
|
||||
yyz
|
||||
Zenos
|
||||
zizmor
|
||||
zombocom
|
||||
zos
|
||||
|
||||
6
.github/dependabot.yml
vendored
6
.github/dependabot.yml
vendored
@@ -8,8 +8,6 @@ updates:
|
||||
github-actions:
|
||||
patterns:
|
||||
- "*"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
|
||||
- package-ecosystem: gomod
|
||||
directory: /
|
||||
@@ -19,8 +17,6 @@ updates:
|
||||
gomod:
|
||||
patterns:
|
||||
- "*"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
|
||||
- package-ecosystem: npm
|
||||
directory: /
|
||||
@@ -30,5 +26,3 @@ updates:
|
||||
npm:
|
||||
patterns:
|
||||
- "*"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
|
||||
9
.github/workflows/asset-verification.yml
vendored
9
.github/workflows/asset-verification.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
asset_verification:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -24,10 +24,11 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||
with:
|
||||
node-version: '24.11.0'
|
||||
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
node-version: latest
|
||||
|
||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
with:
|
||||
go-version: '1.25.4'
|
||||
go-version: stable
|
||||
|
||||
- name: install node deps
|
||||
run: |
|
||||
|
||||
9
.github/workflows/docker-pr.yml
vendored
9
.github/workflows/docker-pr.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
fetch-tags: true
|
||||
fetch-depth: 0
|
||||
@@ -28,10 +28,11 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||
with:
|
||||
node-version: '24.11.0'
|
||||
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
node-version: latest
|
||||
|
||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
with:
|
||||
go-version: '1.25.4'
|
||||
go-version: stable
|
||||
|
||||
- uses: ko-build/setup-ko@d006021bd0c28d1ce33a07e7943d48b079944c8d # v0.9
|
||||
|
||||
|
||||
9
.github/workflows/docker.yml
vendored
9
.github/workflows/docker.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
fetch-tags: true
|
||||
fetch-depth: 0
|
||||
@@ -38,10 +38,11 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||
with:
|
||||
node-version: '24.11.0'
|
||||
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
node-version: latest
|
||||
|
||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
with:
|
||||
go-version: '1.25.4'
|
||||
go-version: stable
|
||||
|
||||
- uses: ko-build/setup-ko@d006021bd0c28d1ce33a07e7943d48b079944c8d # v0.9
|
||||
|
||||
|
||||
6
.github/workflows/docs-deploy.yml
vendored
6
.github/workflows/docs-deploy.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -53,14 +53,14 @@ jobs:
|
||||
push: true
|
||||
|
||||
- name: Apply k8s manifests to limsa lominsa
|
||||
uses: actions-hub/kubectl@1d2c1e96fe0ae23b0c95ee8240ae151b1e638c23 # v1.34.2
|
||||
uses: actions-hub/kubectl@f14933a23bc8c582b5aa7d108defd8e2cb9fa86d # v1.34.1
|
||||
env:
|
||||
KUBE_CONFIG: ${{ secrets.LIMSA_LOMINSA_KUBECONFIG }}
|
||||
with:
|
||||
args: apply -k docs/manifest
|
||||
|
||||
- name: Apply k8s manifests to limsa lominsa
|
||||
uses: actions-hub/kubectl@1d2c1e96fe0ae23b0c95ee8240ae151b1e638c23 # v1.34.2
|
||||
uses: actions-hub/kubectl@f14933a23bc8c582b5aa7d108defd8e2cb9fa86d # v1.34.1
|
||||
env:
|
||||
KUBE_CONFIG: ${{ secrets.LIMSA_LOMINSA_KUBECONFIG }}
|
||||
with:
|
||||
|
||||
2
.github/workflows/docs-test.yml
vendored
2
.github/workflows/docs-test.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
76
.github/workflows/go-mod-tidy-check.yml
vendored
76
.github/workflows/go-mod-tidy-check.yml
vendored
@@ -1,76 +0,0 @@
|
||||
name: Go Mod Tidy Check
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
go_mod_tidy_check:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
with:
|
||||
go-version: '1.25.4'
|
||||
|
||||
- name: Check go.mod and go.sum in main directory
|
||||
run: |
|
||||
# Store original file state
|
||||
cp go.mod go.mod.orig
|
||||
cp go.sum go.sum.orig
|
||||
|
||||
# Run go mod tidy
|
||||
go mod tidy
|
||||
|
||||
# Check if files changed
|
||||
if ! diff -q go.mod.orig go.mod > /dev/null 2>&1; then
|
||||
echo "ERROR: go.mod in main directory has changed after running 'go mod tidy'"
|
||||
echo "Please run 'go mod tidy' locally and commit the changes"
|
||||
diff go.mod.orig go.mod
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! diff -q go.sum.orig go.sum > /dev/null 2>&1; then
|
||||
echo "ERROR: go.sum in main directory has changed after running 'go mod tidy'"
|
||||
echo "Please run 'go mod tidy' locally and commit the changes"
|
||||
diff go.sum.orig go.sum
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "SUCCESS: go.mod and go.sum in main directory are tidy"
|
||||
|
||||
- name: Check go.mod and go.sum in test directory
|
||||
run: |
|
||||
cd test
|
||||
|
||||
# Store original file state
|
||||
cp go.mod go.mod.orig
|
||||
cp go.sum go.sum.orig
|
||||
|
||||
# Run go mod tidy
|
||||
go mod tidy
|
||||
|
||||
# Check if files changed
|
||||
if ! diff -q go.mod.orig go.mod > /dev/null 2>&1; then
|
||||
echo "ERROR: go.mod in test directory has changed after running 'go mod tidy'"
|
||||
echo "Please run 'go mod tidy' locally and commit the changes"
|
||||
diff go.mod.orig go.mod
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! diff -q go.sum.orig go.sum > /dev/null 2>&1; then
|
||||
echo "ERROR: go.sum in test directory has changed after running 'go mod tidy'"
|
||||
echo "Please run 'go mod tidy' locally and commit the changes"
|
||||
diff go.sum.orig go.sum
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "SUCCESS: go.mod and go.sum in test directory are tidy"
|
||||
9
.github/workflows/go.yml
vendored
9
.github/workflows/go.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
#runs-on: alrest-techarohq
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -26,10 +26,11 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||
with:
|
||||
node-version: '24.11.0'
|
||||
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
node-version: latest
|
||||
|
||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
with:
|
||||
go-version: '1.25.4'
|
||||
go-version: stable
|
||||
|
||||
- name: Cache playwright binaries
|
||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
|
||||
9
.github/workflows/package-builds-stable.yml
vendored
9
.github/workflows/package-builds-stable.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
#runs-on: alrest-techarohq
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-tags: true
|
||||
@@ -27,10 +27,11 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||
with:
|
||||
node-version: '24.11.0'
|
||||
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
node-version: latest
|
||||
|
||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
with:
|
||||
go-version: '1.25.4'
|
||||
go-version: stable
|
||||
|
||||
- name: install node deps
|
||||
run: |
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
#runs-on: alrest-techarohq
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-tags: true
|
||||
@@ -28,10 +28,11 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||
with:
|
||||
node-version: '24.11.0'
|
||||
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
node-version: latest
|
||||
|
||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
with:
|
||||
go-version: '1.25.4'
|
||||
go-version: stable
|
||||
|
||||
- name: install node deps
|
||||
run: |
|
||||
|
||||
9
.github/workflows/smoke-tests.yml
vendored
9
.github/workflows/smoke-tests.yml
vendored
@@ -29,16 +29,17 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||
with:
|
||||
node-version: '24.11.0'
|
||||
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
node-version: latest
|
||||
|
||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
with:
|
||||
go-version: '1.25.4'
|
||||
go-version: stable
|
||||
|
||||
- uses: ko-build/setup-ko@d006021bd0c28d1ce33a07e7943d48b079944c8d # v0.9
|
||||
|
||||
|
||||
2
.github/workflows/ssh-ci-runner-cron.yml
vendored
2
.github/workflows/ssh-ci-runner-cron.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
fetch-tags: true
|
||||
fetch-depth: 0
|
||||
|
||||
6
.github/workflows/ssh-ci.yml
vendored
6
.github/workflows/ssh-ci.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
- aarch64-16k
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
fetch-tags: true
|
||||
fetch-depth: 0
|
||||
@@ -35,9 +35,9 @@ jobs:
|
||||
name: id_rsa
|
||||
known_hosts: ${{ secrets.CI_SSH_KNOWN_HOSTS }}
|
||||
|
||||
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
with:
|
||||
go-version: '1.25.4'
|
||||
go-version: stable
|
||||
|
||||
- name: Run CI
|
||||
run: go run ./utils/cmd/backoff-retry bash test/ssh-ci/rigging.sh ${{ matrix.host }}
|
||||
|
||||
4
.github/workflows/zizmor.yml
vendored
4
.github/workflows/zizmor.yml
vendored
@@ -16,12 +16,12 @@ jobs:
|
||||
security-events: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||
uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2
|
||||
|
||||
- name: Run zizmor 🌈
|
||||
run: uvx zizmor --format sarif . > results.sarif
|
||||
|
||||
@@ -50,7 +50,8 @@ bots:
|
||||
# user_agent_regex: (?i:bot|crawler)
|
||||
# action: CHALLENGE
|
||||
# challenge:
|
||||
# difficulty: 16 # impossible
|
||||
# difficulty: 16 # impossible
|
||||
# report_as: 4 # lie to the operator
|
||||
# algorithm: slow # intentionally waste CPU cycles and time
|
||||
|
||||
# Requires a subscription to Thoth to use, see
|
||||
@@ -248,6 +249,7 @@ thresholds:
|
||||
# https://anubis.techaro.lol/docs/admin/configuration/challenges/metarefresh
|
||||
algorithm: metarefresh
|
||||
difficulty: 1
|
||||
report_as: 1
|
||||
# For clients that are browser-like but have either gained points from custom rules or
|
||||
# report as a standard browser.
|
||||
- name: moderate-suspicion
|
||||
@@ -260,6 +262,7 @@ thresholds:
|
||||
# https://anubis.techaro.lol/docs/admin/configuration/challenges/proof-of-work
|
||||
algorithm: fast
|
||||
difficulty: 2 # two leading zeros, very fast for most clients
|
||||
report_as: 2
|
||||
- name: mild-proof-of-work
|
||||
expression:
|
||||
all:
|
||||
@@ -270,6 +273,7 @@ thresholds:
|
||||
# https://anubis.techaro.lol/docs/admin/configuration/challenges/proof-of-work
|
||||
algorithm: fast
|
||||
difficulty: 4
|
||||
report_as: 4
|
||||
# For clients that are browser like and have gained many points from custom rules
|
||||
- name: extreme-suspicion
|
||||
expression: weight >= 30
|
||||
@@ -278,3 +282,4 @@ thresholds:
|
||||
# https://anubis.techaro.lol/docs/admin/configuration/challenges/proof-of-work
|
||||
algorithm: fast
|
||||
difficulty: 6
|
||||
report_as: 6
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
- name: telegrambot
|
||||
action: ALLOW
|
||||
expression:
|
||||
all:
|
||||
- userAgent.matches("TelegramBot")
|
||||
- verifyFCrDNS(remoteAddress, "ptr\\.telegram\\.org$")
|
||||
@@ -1,6 +0,0 @@
|
||||
- name: vkbot
|
||||
action: ALLOW
|
||||
expression:
|
||||
all:
|
||||
- userAgent.matches("vkShare[^+]+\\+http\\://vk\\.com/dev/Share")
|
||||
- verifyFCrDNS(remoteAddress, "^snipster\\d+\\.go\\.mail\\.ru$")
|
||||
@@ -8,4 +8,3 @@
|
||||
- import: (data)/crawlers/marginalia.yaml
|
||||
- import: (data)/crawlers/mojeekbot.yaml
|
||||
- import: (data)/crawlers/commoncrawl.yaml
|
||||
- import: (data)/crawlers/yandexbot.yaml
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
- name: yandexbot
|
||||
action: ALLOW
|
||||
expression:
|
||||
all:
|
||||
- userAgent.matches("\\+http\\://yandex\\.com/bots")
|
||||
- verifyFCrDNS(remoteAddress, "^.*\\.yandex\\.(ru|com|net)$")
|
||||
@@ -35,6 +35,7 @@
|
||||
# action: CHALLENGE
|
||||
# challenge:
|
||||
# difficulty: 16 # impossible
|
||||
# report_as: 4 # lie to the operator
|
||||
# algorithm: slow # intentionally waste CPU cycles and time
|
||||
|
||||
# Requires a subscription to Thoth to use, see
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
- import: (data)/clients/telegram-preview.yaml
|
||||
- import: (data)/clients/vk-preview.yaml
|
||||
@@ -13,10 +13,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
<!-- This changes the project to: -->
|
||||
|
||||
## v1.24.0 [pre1]: Y'shtola Rhul
|
||||
|
||||
Anubis is back and better than ever! Lots of minor fixes with some big ones interspersed.
|
||||
|
||||
- Fix panic when validating challenges after privacy-mode browsers strip headers and the follow-up request matches an `ALLOW` threshold.
|
||||
- Expose WEIGHT rule matches as Prometheus metrics.
|
||||
- Allow more OCI registry clients [based on feedback](https://github.com/TecharoHQ/anubis/pull/1253#issuecomment-3506744184).
|
||||
@@ -28,42 +24,6 @@ Anubis is back and better than ever! Lots of minor fixes with some big ones inte
|
||||
- Open Graph passthrough now reuses the configured target Host/SNI/TLS settings, so metadata fetches succeed when the upstream certificate differs from the public domain. ([1283](https://github.com/TecharoHQ/anubis/pull/1283))
|
||||
- Stabilize the CVE-2025-24369 regression test by always submitting an invalid proof instead of relying on random POW failures.
|
||||
|
||||
### Deprecate `report_as` in challenge configuration
|
||||
|
||||
Previously Anubis let you lie to users about the difficulty of a challenge to interfere with operators of malicious scrapers as a psychological attack:
|
||||
|
||||
```yaml
|
||||
bots:
|
||||
# Punish any bot with "bot" in the user-agent string
|
||||
# This is known to have a high false-positive rate, use at your own risk
|
||||
- name: generic-bot-catchall
|
||||
user_agent_regex: (?i:bot|crawler)
|
||||
action: CHALLENGE
|
||||
challenge:
|
||||
difficulty: 16 # impossible
|
||||
report_as: 4 # lie to the operator
|
||||
algorithm: slow # intentionally waste CPU cycles and time
|
||||
```
|
||||
|
||||
This has turned out to be a bad idea because it has caused massive user experience problems and has been removed. If you are using this setting, you will get a warning in your logs like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"time": "2025-11-25T23:10:31.092201549-05:00",
|
||||
"level": "WARN",
|
||||
"source": {
|
||||
"function": "github.com/TecharoHQ/anubis/lib/policy.ParseConfig",
|
||||
"file": "/home/xe/code/TecharoHQ/anubis/lib/policy/policy.go",
|
||||
"line": 201
|
||||
},
|
||||
"msg": "use of deprecated report_as setting detected, please remove this from your policy file when possible",
|
||||
"at": "config-validate",
|
||||
"name": "mild-suspicion"
|
||||
}
|
||||
```
|
||||
|
||||
To remove this warning, remove this setting from your policy file.
|
||||
|
||||
### Logging customization
|
||||
|
||||
Anubis now supports the ability to log to multiple backends ("sinks"). This allows you to have Anubis [log to a file](./admin/policies.mdx#file-sink) instead of just logging to standard out. You can also customize the [logging level](./admin/policies.mdx#log-levels) in the policy file:
|
||||
@@ -84,33 +44,6 @@ logging:
|
||||
|
||||
Additionally, information about [how Anubis uses each logging level](./admin/policies.mdx#log-levels) has been added to the documentation.
|
||||
|
||||
### DNS Features
|
||||
|
||||
- CEL expressions for:
|
||||
- FCrDNS checks
|
||||
- Forward DNS queries
|
||||
- Reverse DNS queries
|
||||
- `arpaReverseIP` to transform IPv4/6 addresses into ARPA reverse IP notation.
|
||||
- `regexSafe` to escape regex special characters (useful for including `remoteAddress` or headers in regular expressions).
|
||||
- DNS cache and other optimizations to minimize unnecessary DNS queries.
|
||||
|
||||
The DNS cache TTL can be changed in the bots config like this:
|
||||
|
||||
```yaml
|
||||
dns_ttl:
|
||||
forward: 600
|
||||
reverse: 600
|
||||
```
|
||||
|
||||
The default value for both forward and reverse queries is 300 seconds.
|
||||
|
||||
The `verifyFCrDNS` CEL function has two overloads:
|
||||
|
||||
- `(addr)`
|
||||
Simply verifies that the remote side has PTR records pointing to the target address.
|
||||
- `(addr, ptrPattern)`
|
||||
Verifies that the remote side refers to a specific domain and that this domain points to the target IP.
|
||||
|
||||
## v1.23.1: Lyse Hext - Echo 1
|
||||
|
||||
- Fix `SERVE_ROBOTS_TXT` setting after the double slash fix broke it.
|
||||
|
||||
@@ -12,6 +12,7 @@ To use it in your Anubis configuration:
|
||||
action: CHALLENGE
|
||||
challenge:
|
||||
difficulty: 1 # Number of seconds to wait before refreshing the page
|
||||
report_as: 4 # Unused by this challenge method
|
||||
algorithm: metarefresh # Specify a non-JS challenge method
|
||||
```
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ To use it in your Anubis configuration:
|
||||
action: CHALLENGE
|
||||
challenge:
|
||||
difficulty: 1 # Number of seconds to wait before refreshing the page
|
||||
report_as: 4 # Unused by this challenge method
|
||||
algorithm: preact
|
||||
```
|
||||
|
||||
|
||||
@@ -233,27 +233,6 @@ This is best applied when doing explicit block rules, eg:
|
||||
|
||||
It seems counter-intuitive to allow known bad clients through sometimes, but this allows you to confuse attackers by making Anubis' behavior random. Adjust the thresholds and numbers as facts and circumstances demand.
|
||||
|
||||
### `regexSafe`
|
||||
|
||||
Available in `bot` expressions.
|
||||
|
||||
```ts
|
||||
function regexSafe(input: string): string;
|
||||
```
|
||||
|
||||
`regexSafe` takes a string and escapes it for safe use inside of a regular expression. This is useful when you are creating regular expressions from headers or variables such as `remoteAddress`.
|
||||
|
||||
| Input | Output |
|
||||
| :------------------------ | :------------------------------ |
|
||||
| `regexSafe("1.2.3.4")` | `1\\.2\\.3\\.4` |
|
||||
| `regexSafe("techaro.lol")` | `techaro\\.lol` |
|
||||
| `regexSafe("star*")` | `star\\*` |
|
||||
| `regexSafe("plus+")` | `plus\\+` |
|
||||
| `regexSafe("{braces}")` | `\\{braces\\}` |
|
||||
| `regexSafe("start^")` | `start\\^` |
|
||||
| `regexSafe("back\\slash")` | `back\\\\slash` |
|
||||
| `regexSafe("dash-dash")` | `dash\\-dash` |
|
||||
|
||||
### `segments`
|
||||
|
||||
Available in `bot` expressions.
|
||||
@@ -287,99 +266,6 @@ This is useful if you want to write rules that allow requests that have no query
|
||||
- size(segments(path)) < 2
|
||||
```
|
||||
|
||||
### DNS Functions
|
||||
|
||||
Anubis can also perform DNS lookups as a part of its expression evaluation. This can be useful for doing things like checking for a valid [Forward-confirmed reverse DNS (FCrDNS)](https://en.wikipedia.org/wiki/Forward-confirmed_reverse_DNS) record.
|
||||
|
||||
#### `arpaReverseIP`
|
||||
|
||||
Available in `bot` expressions.
|
||||
|
||||
```ts
|
||||
function arpaReverseIP(ip: string): string;
|
||||
```
|
||||
|
||||
`arpaReverseIP` takes an IP address and returns its value in [ARPA notation](https://www.ietf.org/rfc/rfc2317.html). This can be useful when matching PTR record patterns.
|
||||
|
||||
| Input | Output |
|
||||
| :----------------------------- | :------------------------------------------------------------------- |
|
||||
| `arpaReverseIP("1.2.3.4")` | `4.3.2.1` |
|
||||
| `arpaReverseIP("2001:db8::1")` | `1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2` |
|
||||
|
||||
#### `lookupHost`
|
||||
|
||||
Available in `bot` expressions.
|
||||
|
||||
```ts
|
||||
function lookupHost(host: string): string[];
|
||||
```
|
||||
|
||||
`lookupHost` performs a DNS lookup for the given hostname and returns a list of IP addresses.
|
||||
|
||||
```yaml
|
||||
- name: cloudflare-ip-in-host-header
|
||||
action: DENY
|
||||
expression: '"104.16.0.0" in lookupHost(headers["Host"])'
|
||||
```
|
||||
|
||||
#### `reverseDNS`
|
||||
|
||||
Available in `bot` expressions.
|
||||
|
||||
```ts
|
||||
function reverseDNS(ip: string): string[];
|
||||
```
|
||||
|
||||
`reverseDNS` takes an IP address and returns the DNS names associated with it. This is useful when you want to check PTR records of an IP address.
|
||||
|
||||
```yaml
|
||||
- name: allow-googlebot
|
||||
action: ALLOW
|
||||
expression: 'reverseDNS(remoteAddress).endsWith(".googlebot.com")'
|
||||
```
|
||||
|
||||
::: warning
|
||||
|
||||
Do not use this for validating the legitimacy of an IP address. It is possible for DNS records to be out of date or otherwise manipulated. Use [`verifyFCrDNS`](#verifyfcrdns) instead for a more reliable result.
|
||||
|
||||
:::
|
||||
|
||||
#### `verifyFCrDNS`
|
||||
|
||||
Available in `bot` expressions.
|
||||
|
||||
```ts
|
||||
function verifyFCrDNS(ip: string): bool;
|
||||
function verifyFCrDNS(ip: string, pattern: string): bool;
|
||||
```
|
||||
|
||||
`verifyFCrDNS` checks if the reverse DNS of an IP address matches its forward DNS. This is a common technique to filter out spam and bot traffic. `verifyFCrDNS` comes in two forms:
|
||||
|
||||
- `verifyFCrDNS(remoteAddress)` will check that the reverse DNS of the remote address resolves back to the remote address. If no PTR records, returns true.
|
||||
- `verifyFCrDNS(remoteAddress, pattern)` will check that the reverse DNS of the remote address is matching with pattern and that name resolves back to the remote address.
|
||||
|
||||
This is best used in rules like this:
|
||||
|
||||
```yaml
|
||||
- name: require-fcrdns-for-post
|
||||
action: DENY
|
||||
expression:
|
||||
all:
|
||||
- method == "POST"
|
||||
- "!verifyFCrDNS(remoteAddress)"
|
||||
```
|
||||
|
||||
Here is an another example that allows requests from telegram:
|
||||
|
||||
```yaml
|
||||
- name: telegrambot
|
||||
action: ALLOW
|
||||
expression:
|
||||
all:
|
||||
- userAgent.matches("TelegramBot")
|
||||
- verifyFCrDNS(remoteAddress, "ptr\\.telegram\\.org$")
|
||||
```
|
||||
|
||||
## Life advice
|
||||
|
||||
Expressions are very powerful. This is a benefit and a burden. If you are not careful with your expression targeting, you will be liable to get yourself into trouble. If you are at all in doubt, throw a `CHALLENGE` over a `DENY`. Legitimate users can easily work around a `CHALLENGE` result with a [proof of work challenge](../../design/why-proof-of-work.mdx). Bots are less likely to be able to do this.
|
||||
|
||||
@@ -156,68 +156,3 @@ server {
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Caddy
|
||||
|
||||
Anubis can be used with the [`forward_auth`](https://caddyserver.com/docs/caddyfile/directives/forward_auth) directive in Caddy.
|
||||
|
||||
First, the `TARGET` environment variable in Anubis must be set to a space, eg:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="env-file" label="Environment file" default>
|
||||
|
||||
```shell
|
||||
# anubis.env
|
||||
|
||||
TARGET=" "
|
||||
# ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="docker-compose" label="Docker Compose">
|
||||
|
||||
```yaml
|
||||
services:
|
||||
anubis-caddy:
|
||||
image: ghcr.io/techarohq/anubis:latest
|
||||
environment:
|
||||
TARGET: " "
|
||||
# ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="k8s" label="Kubernetes">
|
||||
|
||||
Inside your Deployment, StatefulSet, or Pod:
|
||||
|
||||
```yaml
|
||||
- name: anubis
|
||||
image: ghcr.io/techarohq/anubis:latest
|
||||
env:
|
||||
- name: TARGET
|
||||
value: " "
|
||||
# ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Then configure the necessary directives in your site block:
|
||||
|
||||
```caddy
|
||||
route {
|
||||
# Assumption: Anubis is running in the same network namespace as
|
||||
# caddy on localhost TCP port 8923
|
||||
reverse_proxy /.within.website/* 127.0.0.1:8923
|
||||
forward_auth 127.0.0.1:8923 {
|
||||
uri /.within.website/x/cmd/anubis/api/check
|
||||
trusted_proxies private_ranges
|
||||
@unauthorized status 401
|
||||
handle_response @unauthorized {
|
||||
redir * /.within.website/?redir={uri} 307
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you want to use this for multiple sites, you can create a [snippet](https://caddyserver.com/docs/caddyfile/concepts#snippets) and import it in multiple site blocks.
|
||||
|
||||
@@ -41,6 +41,7 @@ thresholds:
|
||||
challenge:
|
||||
algorithm: metarefresh
|
||||
difficulty: 1
|
||||
report_as: 1
|
||||
|
||||
- name: moderate-suspicion
|
||||
expression:
|
||||
@@ -51,6 +52,7 @@ thresholds:
|
||||
challenge:
|
||||
algorithm: fast
|
||||
difficulty: 2
|
||||
report_as: 2
|
||||
|
||||
- name: extreme-suspicion
|
||||
expression: weight >= 20
|
||||
@@ -58,6 +60,7 @@ thresholds:
|
||||
challenge:
|
||||
algorithm: fast
|
||||
difficulty: 4
|
||||
report_as: 4
|
||||
```
|
||||
|
||||
This defines a suite of 4 thresholds:
|
||||
@@ -127,6 +130,7 @@ action: CHALLENGE
|
||||
challenge:
|
||||
algorithm: metarefresh
|
||||
difficulty: 1
|
||||
report_as: 1
|
||||
```
|
||||
|
||||
</td>
|
||||
|
||||
@@ -92,11 +92,6 @@ Assuming you are protecting `anubistest.techaro.lol`, you need the following ser
|
||||
DocumentRoot /var/www/anubistest.techaro.lol
|
||||
ErrorLog /var/log/httpd/anubistest.techaro.lol_error.log
|
||||
CustomLog /var/log/httpd/anubistest.techaro.lol_access.log combined
|
||||
|
||||
# Pass the remote IP to the proxied application instead of 127.0.0.1
|
||||
# This requires mod_remoteip
|
||||
RemoteIPHeader X-Real-IP
|
||||
RemoteIPTrustedProxy 127.0.0.1/32
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ This rule has been known to have a high false positive rate in testing. Please u
|
||||
action: CHALLENGE
|
||||
challenge:
|
||||
difficulty: 16 # impossible
|
||||
report_as: 4 # lie to the operator
|
||||
algorithm: slow # intentionally waste CPU cycles and time
|
||||
```
|
||||
|
||||
@@ -92,6 +93,7 @@ Challenges can be configured with these settings:
|
||||
| Key | Example | Description |
|
||||
| :----------- | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `difficulty` | `4` | The challenge difficulty (number of leading zeros) for proof-of-work. See [Why does Anubis use Proof-of-Work?](/docs/design/why-proof-of-work) for more details. |
|
||||
| `report_as` | `4` | What difficulty the UI should report to the user. Useful for messing with industrial-scale scraping efforts. |
|
||||
| `algorithm` | `"fast"` | The challenge method to use. See [the list of challenge methods](./configuration/challenges/) for more information. |
|
||||
|
||||
### Remote IP based filtering
|
||||
|
||||
@@ -49,6 +49,7 @@ bots:
|
||||
# action: CHALLENGE
|
||||
# challenge:
|
||||
# difficulty: 16 # impossible
|
||||
# report_as: 4 # lie to the operator
|
||||
# algorithm: slow # intentionally waste CPU cycles and time
|
||||
|
||||
- name: rss-feed-blog
|
||||
@@ -104,6 +105,7 @@ thresholds:
|
||||
# https://anubis.techaro.lol/docs/admin/configuration/challenges/metarefresh
|
||||
algorithm: metarefresh
|
||||
difficulty: 1
|
||||
report_as: 1
|
||||
# For clients that are browser-like but have either gained points from custom rules or
|
||||
# report as a standard browser.
|
||||
- name: moderate-suspicion
|
||||
@@ -120,6 +122,7 @@ thresholds:
|
||||
# challenge data, and forwards that to the client.
|
||||
algorithm: preact
|
||||
difficulty: 1
|
||||
report_as: 1
|
||||
- name: mild-proof-of-work
|
||||
expression:
|
||||
all:
|
||||
@@ -130,6 +133,7 @@ thresholds:
|
||||
# https://anubis.techaro.lol/docs/admin/configuration/challenges/proof-of-work
|
||||
algorithm: fast
|
||||
difficulty: 2 # two leading zeros, very fast for most clients
|
||||
report_as: 2
|
||||
# For clients that are browser like and have gained many points from custom rules
|
||||
- name: extreme-suspicion
|
||||
expression: weight >= 30
|
||||
@@ -138,6 +142,7 @@ thresholds:
|
||||
# https://anubis.techaro.lol/docs/admin/configuration/challenges/proof-of-work
|
||||
algorithm: fast
|
||||
difficulty: 4
|
||||
report_as: 4
|
||||
|
||||
dnsbl: false
|
||||
|
||||
|
||||
41
go.mod
41
go.mod
@@ -5,9 +5,9 @@ go 1.24.2
|
||||
require (
|
||||
github.com/TecharoHQ/thoth-proto v0.5.0
|
||||
github.com/a-h/templ v0.3.960
|
||||
github.com/aws/aws-sdk-go-v2 v1.40.0
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.1
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.92.0
|
||||
github.com/aws/aws-sdk-go-v2 v1.39.6
|
||||
github.com/aws/aws-sdk-go-v2/config v1.31.20
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.2
|
||||
github.com/cespare/xxhash/v2 v2.3.0
|
||||
github.com/facebookgo/flagenv v0.0.0-20160425205200-fcd59fca7456
|
||||
github.com/fahedouch/go-logrotate v0.3.0
|
||||
@@ -22,14 +22,14 @@ require (
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.0
|
||||
github.com/playwright-community/playwright-go v0.5200.1
|
||||
github.com/prometheus/client_golang v1.23.2
|
||||
github.com/redis/go-redis/v9 v9.17.0
|
||||
github.com/redis/go-redis/v9 v9.16.0
|
||||
github.com/sebest/xff v0.0.0-20210106013422-671bd2870b3a
|
||||
github.com/shirou/gopsutil/v4 v4.25.10
|
||||
github.com/testcontainers/testcontainers-go v0.40.0
|
||||
go.etcd.io/bbolt v1.4.3
|
||||
golang.org/x/net v0.47.0
|
||||
golang.org/x/text v0.31.0
|
||||
google.golang.org/grpc v1.77.0
|
||||
google.golang.org/grpc v1.76.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/apimachinery v0.34.2
|
||||
sigs.k8s.io/yaml v1.6.0
|
||||
@@ -54,20 +54,19 @@ require (
|
||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.24 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.40.2 // indirect
|
||||
github.com/aws/smithy-go v1.23.2 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect
|
||||
@@ -172,12 +171,12 @@ require (
|
||||
github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
gitlab.com/digitalxero/go-conventional-commit v1.0.7 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect
|
||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.37.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.7.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
|
||||
90
go.sum
90
go.sum
@@ -51,42 +51,40 @@ github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYW
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/aws/aws-sdk-go-v2 v1.40.0 h1:/WMUA0kjhZExjOQN2z3oLALDREea1A7TobfuiBrKlwc=
|
||||
github.com/aws/aws-sdk-go-v2 v1.40.0/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE=
|
||||
github.com/aws/aws-sdk-go-v2 v1.39.6 h1:2JrPCVgWJm7bm83BDwY5z8ietmeJUbh3O2ACnn+Xsqk=
|
||||
github.com/aws/aws-sdk-go-v2 v1.39.6/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3 h1:DHctwEM8P8iTXFxC/QK0MRjwEpWQeM9yzidCRjldUz0=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3/go.mod h1:xdCzcZEtnSTKVDOmUZs4l/j3pSV6rpo1WXl5ugNsL8Y=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.1 h1:iODUDLgk3q8/flEC7ymhmxjfoAnBDwEEYEVyKZ9mzjU=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.1/go.mod h1:xoAgo17AGrPpJBSLg81W+ikM0cpOZG8ad04T2r+d5P0=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.1 h1:JeW+EwmtTE0yXFK8SmklrFh/cGTTXsQJumgMZNlbxfM=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.1/go.mod h1:BOoXiStwTF+fT2XufhO0Efssbi1CNIO/ZXpZu87N0pw=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14 h1:WZVR5DbDgxzA0BJeudId89Kmgy6DIU4ORpxwsVHz0qA=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14/go.mod h1:Dadl9QO0kHgbrH1GRqGiZdYtW5w+IXXaBNCHTIaheM4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14 h1:PZHqQACxYb8mYgms4RZbhZG0a7dPW06xOjmaH0EJC/I=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14/go.mod h1:VymhrMJUWs69D8u0/lZ7jSB6WgaG/NqHi3gX0aYf6U0=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14 h1:bOS19y6zlJwagBfHxs0ESzr1XCOU2KXJCWcq3E2vfjY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14/go.mod h1:1ipeGBMAxZ0xcTm6y6paC2C/J6f6OO7LBODV9afuAyM=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.31.20 h1:/jWF4Wu90EhKCgjTdy1DGxcbcbNrjfBHvksEL79tfQc=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.31.20/go.mod h1:95Hh1Tc5VYKL9NJ7tAkDcqeKt+MCXQB1hQZaRdJIZE0=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.24 h1:iJ2FmPT35EaIB0+kMa6TnQ+PwG5A1prEdAw+PsMzfHg=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.24/go.mod h1:U91+DrfjAiXPDEGYhh/x29o4p0qHX5HDqG7y5VViv64=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 h1:T1brd5dR3/fzNFAQch/iBKeX07/ffu/cLu+q+RuzEWk=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13/go.mod h1:Peg/GBAQ6JDt+RoBf4meB1wylmAipb7Kg2ZFakZTlwk=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 h1:a+8/MLcWlIxo1lF9xaGt3J/u3yOZx+CdSveSNwjhD40=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13/go.mod h1:oGnKwIYZ4XttyU2JWxFrwvhF6YKiK/9/wmE3v3Iu9K8=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 h1:HBSI2kDkMdWz4ZM7FjwE7e/pWDEZ+nR95x8Ztet1ooY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13/go.mod h1:YE94ZoDArI7awZqJzBAZ3PDD2zSfuP7w6P2knOzIn8M=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.14 h1:ITi7qiDSv/mSGDSWNpZ4k4Ve0DQR6Ug2SJQ8zEHoDXg=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.14/go.mod h1:k1xtME53H1b6YpZt74YmwlONMWf4ecM+lut1WQLAF/U=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13 h1:eg/WYAa12vqTphzIdWMzqYRVKKnCboVPRlvaybNCqPA=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13/go.mod h1:/FDdxWhz1486obGrKKC1HONd7krpk38LBt+dutLcN9k=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 h1:x2Ibm/Af8Fi+BH+Hsn9TXGdT+hKbDd5XOTZxTMxDk7o=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3/go.mod h1:IW1jwyrQgMdhisceG8fQLmQIydcT/jWY21rFhzgaKwo=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.5 h1:Hjkh7kE6D81PgrHlE/m9gx+4TyyeLHuY8xJs7yXN5C4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.5/go.mod h1:nPRXgyCfAurhyaTMoBMwRBYBhaHI4lNPAnJmjM0Tslc=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14 h1:FIouAnCE46kyYqyhs0XEBDFFSREtdnr8HQuLPQPLCrY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14/go.mod h1:UTwDc5COa5+guonQU8qBikJo1ZJ4ln2r1MkF7Dqag1E=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.14 h1:FzQE21lNtUor0Fb7QNgnEyiRCBlolLTX/Z1j65S7teM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.14/go.mod h1:s1ydyWG9pm3ZwmmYN21HKyG9WzAZhYVW85wMHs5FV6w=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.92.0 h1:8FshVvnV2sr9kOSAbOnc/vwVmmAwMjOedKH6JW2ddPM=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.92.0/go.mod h1:wYNqY3L02Z3IgRYxOBPH9I1zD9Cjh9hI5QOy/eOjQvw=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.1 h1:BDgIUYGEo5TkayOWv/oBLPphWwNm/A91AebUjAu5L5g=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.1/go.mod h1:iS6EPmNeqCsGo+xQmXv0jIMjyYtQfnwg36zl2FwEouk=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.4 h1:U//SlnkE1wOQiIImxzdY5PXat4Wq+8rlfVEw4Y7J8as=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.4/go.mod h1:av+ArJpoYf3pgyrj6tcehSFW+y9/QvAY8kMooR9bZCw=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.9 h1:LU8S9W/mPDAU9q0FjCLi0TrCheLMGwzbRpvUMwYspcA=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.9/go.mod h1:/j67Z5XBVDx8nZVp9EuFM9/BS5dvBznbqILGuu73hug=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.1 h1:GdGmKtG+/Krag7VfyOXV17xjTCz0i9NT+JnqLTOI5nA=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.1/go.mod h1:6TxbXoDSgBQ225Qd8Q+MbxUxUh6TtNKwbRt/EPS9xso=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4 h1:NvMjwvv8hpGUILarKw7Z4Q0w1H9anXKsesMxtw++MA4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4/go.mod h1:455WPHSwaGj2waRSpQp7TsnpOnBfw8iDfPfbwl7KPJE=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 h1:kDqdFvMY4AtKoACfzIGD8A0+hbT41KTKF//gq7jITfM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13/go.mod h1:lmKuogqSU3HzQCwZ9ZtcqOc5XGMqtDK7OIc2+DxiUEg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13 h1:zhBJXdhWIFZ1acfDYIhu4+LCzdUS2Vbcum7D01dXlHQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13/go.mod h1:JaaOeCE368qn2Hzi3sEzY6FgAZVCIYcC2nwbro2QCh8=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.2 h1:DhdbtDl4FdNlj31+xiRXANxEE+eC7n8JQz+/ilwQ8Uc=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.2/go.mod h1:+wArOOrcHUevqdto9k1tKOF5++YTe9JEcPSc9Tx2ZSw=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.3 h1:NjShtS1t8r5LUfFVtFeI8xLAHQNTa7UI0VawXlrBMFQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.3/go.mod h1:fKvyjJcz63iL/ftA6RaM8sRCtN4r4zl4tjL3qw5ec7k=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.7 h1:gTsnx0xXNQ6SBbymoDvcoRHL+q4l/dAFsQuKfDWSaGc=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.7/go.mod h1:klO+ejMvYsB4QATfEOIXk8WAEwN4N0aBfJpvC+5SZBo=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.40.2 h1:HK5ON3KmQV2HcAunnx4sKLB9aPf3gKGwVAf7xnx0QT0=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.40.2/go.mod h1:E19xDjpzPZC7LS2knI9E6BaRFDK43Eul7vd6rSq2HWk=
|
||||
github.com/aws/smithy-go v1.23.2 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM=
|
||||
github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
@@ -345,8 +343,8 @@ github.com/prometheus/common v0.67.2 h1:PcBAckGFTIHt2+L3I33uNRTlKTplNzFctXcWhPyA
|
||||
github.com/prometheus/common v0.67.2/go.mod h1:63W3KZb1JOKgcjlIr64WW/LvFGAqKPj0atm+knVGEko=
|
||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
||||
github.com/redis/go-redis/v9 v9.17.0 h1:K6E+ZlYN95KSMmZeEQPbU/c++wfmEvfFB17yEAq/VhM=
|
||||
github.com/redis/go-redis/v9 v9.17.0/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=
|
||||
github.com/redis/go-redis/v9 v9.16.0 h1:OotgqgLSRCmzfqChbQyG1PHC3tLNR89DG4jdOERSEP4=
|
||||
github.com/redis/go-redis/v9 v9.16.0/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
@@ -420,24 +418,24 @@ gitlab.com/digitalxero/go-conventional-commit v1.0.7 h1:8/dO6WWG+98PMhlZowt/Yjui
|
||||
gitlab.com/digitalxero/go-conventional-commit v1.0.7/go.mod h1:05Xc2BFsSyC5tKhK0y+P3bs0AwUtNuTp+mTpbCU/DZ0=
|
||||
go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
|
||||
go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
|
||||
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
|
||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
|
||||
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
|
||||
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
|
||||
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
|
||||
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
|
||||
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
||||
go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os=
|
||||
go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
@@ -535,8 +533,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba h1:
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:G5IanEx8/PgI9w6CFcYQf7jMtHQhZruvfM1i3qOqk5U=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba h1:UKgtfRM7Yh93Sya0Fo8ZzhDP4qBckrrxEr2oF5UIVb8=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
|
||||
google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
|
||||
google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
|
||||
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/TecharoHQ/anubis/lib/store"
|
||||
|
||||
_ "github.com/TecharoHQ/anubis/lib/store/all"
|
||||
)
|
||||
|
||||
type DnsCache struct {
|
||||
forward store.JSON[[]string]
|
||||
reverse store.JSON[[]string]
|
||||
forwardTTL time.Duration
|
||||
reverseTTL time.Duration
|
||||
}
|
||||
|
||||
func NewDNSCache(forwardTTL int, reverseTTL int, backend store.Interface) *DnsCache {
|
||||
return &DnsCache{
|
||||
forward: store.JSON[[]string]{
|
||||
Underlying: backend,
|
||||
Prefix: "forwardDNS",
|
||||
},
|
||||
reverse: store.JSON[[]string]{
|
||||
Underlying: backend,
|
||||
Prefix: "reverseDNS",
|
||||
},
|
||||
forwardTTL: time.Duration(forwardTTL) * time.Second,
|
||||
reverseTTL: time.Duration(reverseTTL) * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Dns) getCachedForward(host string) ([]string, bool) {
|
||||
if d.cache == nil {
|
||||
return nil, false
|
||||
}
|
||||
if cached, err := d.cache.forward.Get(d.ctx, host); err == nil {
|
||||
slog.Debug("DNS: forward cache hit", "name", host, "ips", cached)
|
||||
return cached, true
|
||||
}
|
||||
slog.Debug("DNS: forward cache miss", "name", host)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (d *Dns) getCachedReverse(addr string) ([]string, bool) {
|
||||
if d.cache == nil {
|
||||
return nil, false
|
||||
}
|
||||
if cached, err := d.cache.reverse.Get(d.ctx, addr); err == nil {
|
||||
slog.Debug("DNS: reverse cache hit", "addr", addr, "names", cached)
|
||||
return cached, true
|
||||
}
|
||||
slog.Debug("DNS: reverse cache miss", "addr", addr)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (d *Dns) forwardCachePut(host string, entries []string) {
|
||||
if d.cache == nil {
|
||||
return
|
||||
}
|
||||
d.cache.forward.Set(d.ctx, host, entries, d.cache.forwardTTL)
|
||||
}
|
||||
|
||||
func (d *Dns) reverseCachePut(addr string, entries []string) {
|
||||
if d.cache == nil {
|
||||
return
|
||||
}
|
||||
d.cache.reverse.Set(d.ctx, addr, entries, d.cache.reverseTTL)
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
DNSLookupAddr = net.LookupAddr
|
||||
DNSLookupHost = net.LookupHost
|
||||
)
|
||||
|
||||
type Dns struct {
|
||||
cache *DnsCache
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func New(ctx context.Context, cache *DnsCache) *Dns {
|
||||
return &Dns{
|
||||
cache: cache,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// ReverseDNS performs a reverse DNS lookup for the given IP address and trims the trailing dot from the results.
|
||||
func (d *Dns) ReverseDNS(addr string) ([]string, error) {
|
||||
slog.Debug("DNS: performing reverse lookup", "addr", addr)
|
||||
|
||||
if cached, ok := d.getCachedReverse(addr); ok {
|
||||
return cached, nil
|
||||
}
|
||||
|
||||
names, err := DNSLookupAddr(addr)
|
||||
if err != nil {
|
||||
if dnsErr, ok := err.(*net.DNSError); ok && dnsErr.IsNotFound {
|
||||
slog.Debug("DNS: no PTR record found", "addr", addr)
|
||||
return []string{}, nil
|
||||
}
|
||||
slog.Error("DNS: reverse lookup failed", "addr", addr, "err", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
slog.Debug("DNS: reverse lookup successful", "addr", addr, "names", names)
|
||||
|
||||
trimmedNames := make([]string, len(names))
|
||||
for i, name := range names {
|
||||
trimmedNames[i] = strings.TrimSuffix(name, ".")
|
||||
}
|
||||
d.reverseCachePut(addr, trimmedNames)
|
||||
|
||||
return trimmedNames, nil
|
||||
}
|
||||
|
||||
// LookupHost performs a forward DNS lookup for the given hostname.
|
||||
func (d *Dns) LookupHost(host string) ([]string, error) {
|
||||
slog.Debug("DNS: performing forward lookup", "host", host)
|
||||
|
||||
if cached, ok := d.getCachedForward(host); ok {
|
||||
return cached, nil
|
||||
}
|
||||
|
||||
addrs, err := DNSLookupHost(host)
|
||||
if err != nil {
|
||||
if dnsErr, ok := err.(*net.DNSError); ok && dnsErr.IsNotFound {
|
||||
slog.Debug("DNS: no A/AAAA record found", "host", host)
|
||||
return []string{}, nil
|
||||
}
|
||||
slog.Error("DNS: forward lookup failed", "host", host, "err", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
slog.Debug("DNS: forward lookup successful", "host", host, "addrs", addrs)
|
||||
d.forwardCachePut(host, addrs)
|
||||
return addrs, nil
|
||||
}
|
||||
|
||||
// verifyFCrDNSInternal performs the second half of the FCrDNS check, using a
|
||||
// pre-fetched list of names to perform the forward lookups.
|
||||
func (d *Dns) verifyFCrDNSInternal(addr string, names []string) bool {
|
||||
for _, name := range names {
|
||||
if cached, err := d.LookupHost(name); err == nil {
|
||||
if slices.Contains(cached, addr) {
|
||||
slog.Info("DNS: forward lookup confirmed original IP", "name", name, "addr", addr)
|
||||
return true
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
slog.Info("DNS: could not confirm original IP in forward lookups", "addr", addr)
|
||||
return false
|
||||
}
|
||||
|
||||
// VerifyFCrDNS performs a forward-confirmed reverse DNS (FCrDNS) lookup for the given IP address,
|
||||
// optionally matching against a provided pattern.
|
||||
func (d *Dns) VerifyFCrDNS(addr string, pattern *string) bool {
|
||||
var patternVal string
|
||||
if pattern != nil {
|
||||
patternVal = *pattern
|
||||
}
|
||||
slog.Debug("DNS: performing FCrDNS lookup", "addr", addr, "pattern", patternVal)
|
||||
|
||||
names, err := d.ReverseDNS(addr)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if len(names) == 0 {
|
||||
return pattern == nil // If no pattern specified, check is passed
|
||||
}
|
||||
|
||||
// If a pattern is provided, check for a match.
|
||||
if pattern != nil {
|
||||
anyNameMatched := false
|
||||
for _, name := range names {
|
||||
matched, err := regexp.MatchString(*pattern, name)
|
||||
if err != nil {
|
||||
slog.Error("DNS: verifyFCrDNS invalid regex pattern", "err", err)
|
||||
return false // Invalid pattern is a failure.
|
||||
}
|
||||
if matched {
|
||||
anyNameMatched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !anyNameMatched {
|
||||
slog.Debug("DNS: FCrDNS no PTR matches the pattern", "addr", addr, "pattern", *pattern)
|
||||
return false
|
||||
}
|
||||
slog.Debug("DNS: FCrDNS PTR matched pattern, proceeding with forward check", "addr", addr, "pattern", *pattern)
|
||||
}
|
||||
|
||||
// If we're here, either there was no pattern, or the pattern matched.
|
||||
// Proceed with the forward lookup confirmation.
|
||||
return d.verifyFCrDNSInternal(addr, names)
|
||||
}
|
||||
|
||||
// ArpaReverseIP performs translation from ip v4/v6 to arpa reverse notation
|
||||
func (d *Dns) ArpaReverseIP(addr string) (string, error) {
|
||||
ip := net.ParseIP(addr)
|
||||
if ip == nil {
|
||||
return addr, errors.New("invalid IP address")
|
||||
}
|
||||
|
||||
if ipv4 := ip.To4(); ipv4 != nil {
|
||||
return fmt.Sprintf("%d.%d.%d.%d", ipv4[3], ipv4[2], ipv4[1], ipv4[0]), nil
|
||||
}
|
||||
|
||||
ipv6 := ip.To16()
|
||||
if ipv6 == nil {
|
||||
return addr, errors.New("invalid IPv6 address")
|
||||
}
|
||||
|
||||
hexBytes := make([]byte, hex.EncodedLen(len(ipv6)))
|
||||
hex.Encode(hexBytes, ipv6)
|
||||
|
||||
var sb strings.Builder
|
||||
sb.Grow(len(hexBytes)*2 - 1)
|
||||
|
||||
for i := len(hexBytes) - 1; i >= 0; i-- {
|
||||
sb.WriteByte(hexBytes[i])
|
||||
if i > 0 {
|
||||
sb.WriteByte('.')
|
||||
}
|
||||
}
|
||||
return sb.String(), nil
|
||||
}
|
||||
@@ -1,308 +0,0 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/TecharoHQ/anubis/lib/store/memory"
|
||||
)
|
||||
|
||||
// newTestDNS is a helper function to create a new Dns object with an in-memory cache for testing.
|
||||
func newTestDNS(forwardTTL int, reverseTTL int) *Dns {
|
||||
ctx := context.Background()
|
||||
memStore := memory.New(ctx)
|
||||
cache := NewDNSCache(forwardTTL, reverseTTL, memStore)
|
||||
return New(ctx, cache)
|
||||
}
|
||||
|
||||
// mockLookupAddr is a mock implementation of the net.LookupAddr function.
|
||||
func mockLookupAddr(addr string) ([]string, error) {
|
||||
switch addr {
|
||||
case "8.8.8.8":
|
||||
return []string{"dns.google."}, nil
|
||||
case "1.1.1.1":
|
||||
return []string{"one.one.one.one."}, nil
|
||||
case "208.67.222.222":
|
||||
return []string{"resolver1.opendns.com."}, nil
|
||||
case "9.9.9.9":
|
||||
return nil, &net.DNSError{Err: "no such host", Name: "9.9.9.9", IsNotFound: true}
|
||||
case "1.2.3.4":
|
||||
return nil, errors.New("unknown error")
|
||||
default:
|
||||
return nil, &net.DNSError{Err: "no such host", Name: addr, IsNotFound: true}
|
||||
}
|
||||
}
|
||||
|
||||
// mockLookupHost is a mock implementation of the net.LookupHost function.
|
||||
func mockLookupHost(host string) ([]string, error) {
|
||||
switch host {
|
||||
case "dns.google":
|
||||
return []string{"8.8.8.8", "8.8.4.4"}, nil
|
||||
case "one.one.one.one":
|
||||
return []string{"1.1.1.1", "1.0.0.1"}, nil
|
||||
case "resolver1.opendns.com":
|
||||
return []string{"208.67.222.222"}, nil
|
||||
case "example.com":
|
||||
return nil, &net.DNSError{Err: "no such host", Name: "example.com", IsNotFound: true}
|
||||
default:
|
||||
return nil, &net.DNSError{Err: "no such host", Name: host, IsNotFound: true}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
// Before all tests
|
||||
originalLookupAddr := DNSLookupAddr
|
||||
originalLookupHost := DNSLookupHost
|
||||
|
||||
DNSLookupAddr = mockLookupAddr
|
||||
DNSLookupHost = mockLookupHost
|
||||
|
||||
// Run tests
|
||||
exitCode := m.Run()
|
||||
|
||||
// After all tests
|
||||
DNSLookupAddr = originalLookupAddr
|
||||
DNSLookupHost = originalLookupHost
|
||||
|
||||
// Exit
|
||||
if exitCode != 0 {
|
||||
panic(exitCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDns_ArpaReverseIP(t *testing.T) {
|
||||
d := newTestDNS(0, 0)
|
||||
tests := []struct {
|
||||
name string
|
||||
ip string
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
{"ipv4", "192.0.2.1", "1.2.0.192", false},
|
||||
{"ipv6", "2001:db8::1", "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2", false},
|
||||
{"invalid ip", "invalid", "invalid", true},
|
||||
{"ipv4-mapped ipv6", "::ffff:192.0.2.1", "1.2.0.192", false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := d.ArpaReverseIP(tt.ip)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ArpaReverseIP() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("ArpaReverseIP() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDns_ReverseDNS(t *testing.T) {
|
||||
d := newTestDNS(1, 1) // short TTL for testing cache
|
||||
|
||||
// First call - cache miss
|
||||
t.Run("cache miss", func(t *testing.T) {
|
||||
got, err := d.ReverseDNS("8.8.8.8")
|
||||
if err != nil {
|
||||
t.Fatalf("ReverseDNS() error = %v", err)
|
||||
}
|
||||
want := []string{"dns.google"}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("ReverseDNS() = %v, want %v", got, want)
|
||||
}
|
||||
})
|
||||
|
||||
// Second call - cache hit
|
||||
t.Run("cache hit", func(t *testing.T) {
|
||||
// Temporarily replace lookup function to ensure cache is used
|
||||
originalLookupAddr := DNSLookupAddr
|
||||
DNSLookupAddr = func(addr string) ([]string, error) {
|
||||
return nil, errors.New("should not be called")
|
||||
}
|
||||
defer func() { DNSLookupAddr = originalLookupAddr }()
|
||||
|
||||
got, err := d.ReverseDNS("8.8.8.8")
|
||||
if err != nil {
|
||||
t.Fatalf("ReverseDNS() error = %v", err)
|
||||
}
|
||||
want := []string{"dns.google"}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("ReverseDNS() = %v, want %v", got, want)
|
||||
}
|
||||
})
|
||||
|
||||
// Test cache expiration
|
||||
t.Run("cache expiration", func(t *testing.T) {
|
||||
time.Sleep(2 * time.Second)
|
||||
// Now the cache should be expired
|
||||
// We expect the mock to be called again
|
||||
// To test this we will change the mock to return something different
|
||||
originalLookupAddr := DNSLookupAddr
|
||||
DNSLookupAddr = func(addr string) ([]string, error) {
|
||||
if addr == "8.8.8.8" {
|
||||
return []string{"expired.google."}, nil
|
||||
}
|
||||
return mockLookupAddr(addr)
|
||||
}
|
||||
defer func() { DNSLookupAddr = originalLookupAddr }()
|
||||
|
||||
got, err := d.ReverseDNS("8.8.8.8")
|
||||
if err != nil {
|
||||
t.Fatalf("ReverseDNS() error = %v", err)
|
||||
}
|
||||
want := []string{"expired.google"}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("ReverseDNS() = %v, want %v", got, want)
|
||||
}
|
||||
})
|
||||
|
||||
// Test not found
|
||||
t.Run("not found", func(t *testing.T) {
|
||||
got, err := d.ReverseDNS("9.9.9.9")
|
||||
if err != nil {
|
||||
t.Fatalf("ReverseDNS() error = %v", err)
|
||||
}
|
||||
if len(got) != 0 {
|
||||
t.Errorf("ReverseDNS() = %v, want empty slice", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestDns_LookupHost(t *testing.T) {
|
||||
d := newTestDNS(1, 1)
|
||||
|
||||
t.Run("cache miss", func(t *testing.T) {
|
||||
got, err := d.LookupHost("dns.google")
|
||||
if err != nil {
|
||||
t.Fatalf("LookupHost() error = %v", err)
|
||||
}
|
||||
want := []string{"8.8.8.8", "8.8.4.4"}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("LookupHost() = %v, want %v", got, want)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("cache hit", func(t *testing.T) {
|
||||
originalLookupHost := DNSLookupHost
|
||||
DNSLookupHost = func(host string) ([]string, error) {
|
||||
return nil, errors.New("should not be called")
|
||||
}
|
||||
defer func() { DNSLookupHost = originalLookupHost }()
|
||||
|
||||
got, err := d.LookupHost("dns.google")
|
||||
if err != nil {
|
||||
t.Fatalf("LookupHost() error = %v", err)
|
||||
}
|
||||
want := []string{"8.8.8.8", "8.8.4.4"}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("LookupHost() = %v, want %v", got, want)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("cache expiration", func(t *testing.T) {
|
||||
time.Sleep(2 * time.Second)
|
||||
originalLookupHost := DNSLookupHost
|
||||
DNSLookupHost = func(host string) ([]string, error) {
|
||||
if host == "dns.google" {
|
||||
return []string{"9.9.9.9"}, nil
|
||||
}
|
||||
return mockLookupHost(host)
|
||||
}
|
||||
defer func() { DNSLookupHost = originalLookupHost }()
|
||||
|
||||
got, err := d.LookupHost("dns.google")
|
||||
if err != nil {
|
||||
t.Fatalf("LookupHost() error = %v", err)
|
||||
}
|
||||
want := []string{"9.9.9.9"}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("LookupHost() = %v, want %v", got, want)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("not found", func(t *testing.T) {
|
||||
got, err := d.LookupHost("example.com")
|
||||
if err != nil {
|
||||
t.Fatalf("LookupHost() error = %v", err)
|
||||
}
|
||||
if len(got) != 0 {
|
||||
t.Errorf("LookupHost() = %v, want empty slice", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestDns_VerifyFCrDNS(t *testing.T) {
|
||||
d := newTestDNS(1, 1)
|
||||
|
||||
// Helper to convert string to *string
|
||||
p := func(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
ip string
|
||||
pattern *string
|
||||
want bool
|
||||
}{
|
||||
// Cases without pattern
|
||||
{"valid no pattern", "8.8.8.8", nil, true},
|
||||
{"valid partial no pattern", "1.1.1.1", nil, true},
|
||||
{"not found no pattern", "9.9.9.9", nil, true},
|
||||
{"unknown error no pattern", "1.2.3.4", nil, false},
|
||||
|
||||
// Cases with pattern
|
||||
{"valid match", "8.8.8.8", p(`.*\.google$`), true},
|
||||
{"valid no match", "8.8.8.8", p(`\.com$`), false},
|
||||
{"not found with pattern", "9.9.9.9", p(".*"), false},
|
||||
{"unknown error with pattern", "1.2.3.4", p(".*"), false},
|
||||
{"invalid pattern", "8.8.8.8", p(`[`), false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := d.VerifyFCrDNS(tt.ip, tt.pattern); got != tt.want {
|
||||
t.Errorf("VerifyFCrDNS() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("reverse cache hit", func(t *testing.T) {
|
||||
// Prime the cache
|
||||
if got := d.VerifyFCrDNS("8.8.8.8", nil); got != true {
|
||||
t.Fatalf("VerifyFCrDNS() priming failed, got %v, want true", got)
|
||||
}
|
||||
|
||||
// Now test with a failing lookup to ensure cache is used
|
||||
originalLookupAddr := DNSLookupAddr
|
||||
DNSLookupAddr = func(addr string) ([]string, error) {
|
||||
return nil, errors.New("should not be called")
|
||||
}
|
||||
defer func() { DNSLookupAddr = originalLookupAddr }()
|
||||
|
||||
if got := d.VerifyFCrDNS("8.8.8.8", nil); got != true {
|
||||
t.Errorf("VerifyFCrDNS() = %v, want true", got)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("forward cache hit", func(t *testing.T) {
|
||||
// Prime the cache
|
||||
if got := d.VerifyFCrDNS("8.8.8.8", nil); got != true {
|
||||
t.Fatalf("VerifyFCrDNS() priming failed, got %v, want true", got)
|
||||
}
|
||||
|
||||
// Now test with a failing lookup to ensure cache is used
|
||||
originalLookupHost := DNSLookupHost
|
||||
DNSLookupHost = func(host string) ([]string, error) {
|
||||
return nil, errors.New("should not be called")
|
||||
}
|
||||
defer func() { DNSLookupHost = originalLookupHost }()
|
||||
|
||||
if got := d.VerifyFCrDNS("8.8.8.8", nil); got != true {
|
||||
t.Errorf("VerifyFCrDNS() = %v, want true", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -167,8 +167,8 @@ func (s *Server) hydrateChallengeRule(rule *policy.Bot, chall *challenge.Challen
|
||||
if rule.Challenge.Difficulty == 0 {
|
||||
rule.Challenge.Difficulty = chall.Difficulty
|
||||
}
|
||||
if rule.Challenge.ReportAs != 0 {
|
||||
s.logger.Warn("[DEPRECATION] the report_as field in this bot rule is deprecated, see https://github.com/TecharoHQ/anubis/issues/1310 for more information", "bot_name", rule.Name, "difficulty", rule.Challenge.Difficulty, "report_as", rule.Challenge.ReportAs)
|
||||
if rule.Challenge.ReportAs == 0 {
|
||||
rule.Challenge.ReportAs = chall.Difficulty
|
||||
}
|
||||
if rule.Challenge.Algorithm == "" {
|
||||
rule.Challenge.Algorithm = chall.Method
|
||||
@@ -648,6 +648,7 @@ func (s *Server) check(r *http.Request, lg *slog.Logger) (policy.CheckResult, *p
|
||||
return cr("default/allow", config.RuleAllow, weight), &policy.Bot{
|
||||
Challenge: &config.ChallengeRules{
|
||||
Difficulty: s.policy.DefaultDifficulty,
|
||||
ReportAs: s.policy.DefaultDifficulty,
|
||||
Algorithm: config.DefaultAlgorithm,
|
||||
},
|
||||
Rules: &checker.List{},
|
||||
|
||||
@@ -464,6 +464,10 @@ func TestCheckDefaultDifficultyMatchesPolicy(t *testing.T) {
|
||||
if bot.Challenge.Difficulty != i {
|
||||
t.Errorf("Challenge.Difficulty is wrong, wanted %d, got: %d", i, bot.Challenge.Difficulty)
|
||||
}
|
||||
|
||||
if bot.Challenge.ReportAs != i {
|
||||
t.Errorf("Challenge.ReportAs is wrong, wanted %d, got: %d", i, bot.Challenge.ReportAs)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ func TestBasic(t *testing.T) {
|
||||
Challenge: &config.ChallengeRules{
|
||||
Algorithm: "fast",
|
||||
Difficulty: 0,
|
||||
ReportAs: 0,
|
||||
},
|
||||
}
|
||||
const challengeStr = "hunter"
|
||||
|
||||
@@ -332,7 +332,6 @@ type fileConfig struct {
|
||||
Thresholds []Threshold `json:"thresholds"`
|
||||
StatusCodes StatusCodes `json:"status_codes"`
|
||||
DNSBL bool `json:"dnsbl"`
|
||||
DNSTTL DnsTTL `json:"dns_ttl"`
|
||||
Logging *Logging `json:"logging"`
|
||||
}
|
||||
|
||||
@@ -388,10 +387,6 @@ func Load(fin io.Reader, fname string) (*Config, error) {
|
||||
Challenge: http.StatusOK,
|
||||
Deny: http.StatusOK,
|
||||
},
|
||||
DNSTTL: DnsTTL{
|
||||
Forward: 300,
|
||||
Reverse: 300,
|
||||
},
|
||||
Store: &Store{
|
||||
Backend: "memory",
|
||||
},
|
||||
@@ -407,8 +402,7 @@ func Load(fin io.Reader, fname string) (*Config, error) {
|
||||
}
|
||||
|
||||
result := &Config{
|
||||
DNSBL: c.DNSBL,
|
||||
DNSTTL: c.DNSTTL,
|
||||
DNSBL: c.DNSBL,
|
||||
OpenGraph: OpenGraph{
|
||||
Enabled: c.OpenGraph.Enabled,
|
||||
ConsiderHost: c.OpenGraph.ConsiderHost,
|
||||
@@ -475,29 +469,6 @@ func Load(fin io.Reader, fname string) (*Config, error) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type DnsTTL struct {
|
||||
Forward int `json:"forward"`
|
||||
Reverse int `json:"reverse"`
|
||||
}
|
||||
|
||||
func (sc DnsTTL) Valid() error {
|
||||
var errs []error
|
||||
|
||||
if sc.Forward < 0 {
|
||||
errs = append(errs, fmt.Errorf("%w: forward TTL is %d", ErrStatusCodeNotValid, sc.Forward))
|
||||
}
|
||||
|
||||
if sc.Reverse < 0 {
|
||||
errs = append(errs, fmt.Errorf("%w: reverse TTL is %d", ErrStatusCodeNotValid, sc.Reverse))
|
||||
}
|
||||
|
||||
if len(errs) != 0 {
|
||||
return fmt.Errorf("dns TTL values not valid:\n%w", errors.Join(errs...))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Impressum *Impressum
|
||||
Store *Store
|
||||
@@ -507,7 +478,6 @@ type Config struct {
|
||||
StatusCodes StatusCodes
|
||||
Logging *Logging
|
||||
DNSBL bool
|
||||
DNSTTL DnsTTL
|
||||
}
|
||||
|
||||
func (c Config) Valid() error {
|
||||
|
||||
@@ -110,6 +110,7 @@ func TestBotValid(t *testing.T) {
|
||||
PathRegex: p("Mozilla"),
|
||||
Challenge: &ChallengeRules{
|
||||
Difficulty: -1,
|
||||
ReportAs: 4,
|
||||
Algorithm: "fast",
|
||||
},
|
||||
},
|
||||
@@ -123,6 +124,7 @@ func TestBotValid(t *testing.T) {
|
||||
PathRegex: p("Mozilla"),
|
||||
Challenge: &ChallengeRules{
|
||||
Difficulty: 420,
|
||||
ReportAs: 4,
|
||||
Algorithm: "fast",
|
||||
},
|
||||
},
|
||||
@@ -359,6 +361,7 @@ func TestBotConfigZero(t *testing.T) {
|
||||
|
||||
b.Challenge = &ChallengeRules{
|
||||
Difficulty: 4,
|
||||
ReportAs: 4,
|
||||
Algorithm: DefaultAlgorithm,
|
||||
}
|
||||
if b.Zero() {
|
||||
|
||||
8
lib/config/testdata/bad/dns-ttl-custom.yaml
vendored
8
lib/config/testdata/bad/dns-ttl-custom.yaml
vendored
@@ -1,8 +0,0 @@
|
||||
dns_ttl:
|
||||
forward: 60.0
|
||||
reverse: "600"
|
||||
|
||||
bots:
|
||||
- name: "test"
|
||||
user_agent_regex: ".*"
|
||||
action: "DENY"
|
||||
8
lib/config/testdata/good/dns-ttl-custom.yaml
vendored
8
lib/config/testdata/good/dns-ttl-custom.yaml
vendored
@@ -1,8 +0,0 @@
|
||||
dns_ttl:
|
||||
forward: 600
|
||||
reverse: 600
|
||||
|
||||
bots:
|
||||
- name: "test"
|
||||
user_agent_regex: ".*"
|
||||
action: "DENY"
|
||||
3
lib/config/testdata/good/thresholds.yaml
vendored
3
lib/config/testdata/good/thresholds.yaml
vendored
@@ -18,6 +18,7 @@ thresholds:
|
||||
challenge:
|
||||
algorithm: metarefresh
|
||||
difficulty: 1
|
||||
report_as: 1
|
||||
- name: moderate-suspicion
|
||||
expression:
|
||||
all:
|
||||
@@ -27,9 +28,11 @@ thresholds:
|
||||
challenge:
|
||||
algorithm: fast
|
||||
difficulty: 2
|
||||
report_as: 2
|
||||
- name: extreme-suspicion
|
||||
expression: weight >= 20
|
||||
action: CHALLENGE
|
||||
challenge:
|
||||
algorithm: fast
|
||||
difficulty: 4
|
||||
report_as: 4
|
||||
|
||||
@@ -24,6 +24,7 @@ var (
|
||||
Challenge: &ChallengeRules{
|
||||
Algorithm: "fast",
|
||||
Difficulty: anubis.DefaultDifficulty,
|
||||
ReportAs: anubis.DefaultDifficulty,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ func TestThresholdValid(t *testing.T) {
|
||||
Challenge: &ChallengeRules{
|
||||
Algorithm: "fast",
|
||||
Difficulty: 1,
|
||||
ReportAs: 1,
|
||||
},
|
||||
},
|
||||
err: nil,
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/TecharoHQ/anubis/internal"
|
||||
"github.com/TecharoHQ/anubis/internal/dns"
|
||||
"github.com/TecharoHQ/anubis/lib/config"
|
||||
"github.com/TecharoHQ/anubis/lib/policy/expressions"
|
||||
"github.com/google/cel-go/cel"
|
||||
@@ -17,8 +16,8 @@ type CELChecker struct {
|
||||
src string
|
||||
}
|
||||
|
||||
func NewCELChecker(cfg *config.ExpressionOrList, dnsObj *dns.Dns) (*CELChecker, error) {
|
||||
env, err := expressions.BotEnvironment(dnsObj)
|
||||
func NewCELChecker(cfg *config.ExpressionOrList) (*CELChecker, error) {
|
||||
env, err := expressions.BotEnvironment()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"math/rand/v2"
|
||||
"strings"
|
||||
|
||||
"github.com/TecharoHQ/anubis/internal/dns"
|
||||
"github.com/google/cel-go/cel"
|
||||
"github.com/google/cel-go/common/types"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
@@ -16,7 +15,7 @@ import (
|
||||
// variables and functions that are passed into the CEL scope so that
|
||||
// Anubis can fail loudly and early when something is invalid instead
|
||||
// of blowing up at runtime.
|
||||
func BotEnvironment(dnsObj *dns.Dns) (*cel.Env, error) {
|
||||
func BotEnvironment() (*cel.Env, error) {
|
||||
return New(
|
||||
// Variables exposed to CEL programs:
|
||||
cel.Variable("remoteAddress", cel.StringType),
|
||||
@@ -58,118 +57,6 @@ func BotEnvironment(dnsObj *dns.Dns) (*cel.Env, error) {
|
||||
),
|
||||
),
|
||||
|
||||
cel.Function("reverseDNS",
|
||||
cel.Overload("reverseDNS_string_list_string",
|
||||
[]*cel.Type{cel.StringType},
|
||||
cel.ListType(cel.StringType),
|
||||
cel.UnaryBinding(func(addr ref.Val) ref.Val {
|
||||
addrStr, ok := addr.(types.String)
|
||||
if !ok {
|
||||
return types.ValOrErr(addr, "addr is not a string, but is %T", addr)
|
||||
}
|
||||
|
||||
names, err := dnsObj.ReverseDNS(string(addrStr))
|
||||
if err != nil {
|
||||
return types.NewStringList(types.DefaultTypeAdapter, []string{})
|
||||
}
|
||||
return types.NewStringList(types.DefaultTypeAdapter, names)
|
||||
}),
|
||||
),
|
||||
),
|
||||
|
||||
cel.Function("lookupHost",
|
||||
cel.Overload("lookupHost_string_list_string",
|
||||
[]*cel.Type{cel.StringType},
|
||||
cel.ListType(cel.StringType),
|
||||
cel.UnaryBinding(func(host ref.Val) ref.Val {
|
||||
hostStr, ok := host.(types.String)
|
||||
if !ok {
|
||||
return types.ValOrErr(host, "host is not a string, but is %T", host)
|
||||
}
|
||||
|
||||
addrs, err := dnsObj.LookupHost(string(hostStr))
|
||||
if err != nil {
|
||||
return types.NewStringList(types.DefaultTypeAdapter, []string{})
|
||||
}
|
||||
return types.NewStringList(types.DefaultTypeAdapter, addrs)
|
||||
}),
|
||||
),
|
||||
),
|
||||
|
||||
cel.Function("verifyFCrDNS",
|
||||
cel.Overload("verifyFCrDNS_string_bool",
|
||||
[]*cel.Type{cel.StringType},
|
||||
cel.BoolType,
|
||||
cel.UnaryBinding(func(addr ref.Val) ref.Val {
|
||||
addrStr, ok := addr.(types.String)
|
||||
if !ok {
|
||||
return types.ValOrErr(addr, "addr is not a string")
|
||||
}
|
||||
return types.Bool(dnsObj.VerifyFCrDNS(string(addrStr), nil))
|
||||
}),
|
||||
),
|
||||
cel.Overload("verifyFCrDNS_string_string_bool",
|
||||
[]*cel.Type{cel.StringType, cel.StringType},
|
||||
cel.BoolType,
|
||||
cel.BinaryBinding(func(addr, pattern ref.Val) ref.Val {
|
||||
addrStr, ok := addr.(types.String)
|
||||
if !ok {
|
||||
return types.ValOrErr(addr, "addr is not a string")
|
||||
}
|
||||
patternStr, ok := pattern.(types.String)
|
||||
if !ok {
|
||||
return types.ValOrErr(pattern, "pattern is not a string")
|
||||
}
|
||||
p := string(patternStr)
|
||||
return types.Bool(dnsObj.VerifyFCrDNS(string(addrStr), &p))
|
||||
}),
|
||||
),
|
||||
),
|
||||
|
||||
// arpaReverseIP transforms ip into arpa reverse notation like this
|
||||
// 1.2.3.4 -> 4.3.2.1
|
||||
// 2001:db8::1 -> 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2
|
||||
cel.Function("arpaReverseIP",
|
||||
cel.Overload("arpaReverseIP_string_string",
|
||||
[]*cel.Type{cel.StringType},
|
||||
cel.StringType,
|
||||
cel.UnaryBinding(func(addr ref.Val) ref.Val {
|
||||
s, ok := addr.(types.String)
|
||||
if !ok {
|
||||
return types.ValOrErr(addr, "addr is not a string")
|
||||
}
|
||||
|
||||
reversedIp, err := dnsObj.ArpaReverseIP(string(s))
|
||||
if err != nil {
|
||||
return types.ValOrErr(addr, "%s", err.Error())
|
||||
}
|
||||
return types.String(reversedIp)
|
||||
}),
|
||||
),
|
||||
),
|
||||
|
||||
// regexSafe escapes a string for insertion into a regular expression
|
||||
cel.Function("regexSafe",
|
||||
cel.Overload("regexSafe_string_string",
|
||||
[]*cel.Type{cel.StringType},
|
||||
cel.StringType,
|
||||
cel.UnaryBinding(func(str ref.Val) ref.Val {
|
||||
s, ok := str.(types.String)
|
||||
if !ok {
|
||||
return types.ValOrErr(str, "addr is not a string")
|
||||
}
|
||||
|
||||
escapes := []string{"\\", ".", ":", "*", "?", "-", "[", "]", "(", ")", "+", "{", "}", "|", "^", "$"}
|
||||
r := string(s)
|
||||
|
||||
for _, escape := range escapes {
|
||||
r = strings.ReplaceAll(r, escape, "\\"+escape)
|
||||
}
|
||||
return types.String(r)
|
||||
}),
|
||||
),
|
||||
),
|
||||
|
||||
cel.Function("segments",
|
||||
cel.Overload("segments_string_list_string",
|
||||
[]*cel.Type{cel.StringType},
|
||||
|
||||
@@ -1,29 +1,13 @@
|
||||
package expressions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/TecharoHQ/anubis/internal/dns"
|
||||
"github.com/TecharoHQ/anubis/lib/store/memory"
|
||||
"github.com/google/cel-go/common/types"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
)
|
||||
|
||||
// newTestDNS is a helper function to create a new Dns object with an in-memory cache for testing.
|
||||
func newTestDNS(forwardTTL int, reverseTTL int) *dns.Dns {
|
||||
ctx := context.Background()
|
||||
memStore := memory.New(ctx)
|
||||
cache := dns.NewDNSCache(forwardTTL, reverseTTL, memStore)
|
||||
return dns.New(ctx, cache)
|
||||
}
|
||||
|
||||
func TestBotEnvironment(t *testing.T) {
|
||||
dnsObj := newTestDNS(300, 300)
|
||||
env, err := BotEnvironment(dnsObj)
|
||||
env, err := BotEnvironment()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create bot environment: %v", err)
|
||||
}
|
||||
@@ -251,344 +235,6 @@ func TestBotEnvironment(t *testing.T) {
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("regexSafe", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
expression string
|
||||
expected types.String
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "complex-test",
|
||||
expression: `regexSafe("^(test1|test2|)[a-z]+$")`,
|
||||
expected: types.String("\\^\\(test1\\|test2\\|\\)\\[a\\-z\\]\\+\\$"),
|
||||
description: "should escape all reserved regex characters",
|
||||
},
|
||||
{
|
||||
name: "backslash-test",
|
||||
expression: `regexSafe("use \\\\ for special characters escaping\t, one/\"\\\"/for/cel and one/for/regex")`,
|
||||
expected: types.String("use \\\\\\\\ for special characters escaping\t, one/\"\\\\\"/for/cel and one/for/regex"),
|
||||
description: "should escape double-backslashes as double-double-backslashes and ignore cel escaping and forward slashes",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
prog, err := Compile(env, tt.expression)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to compile expression %q: %v", tt.expression, err)
|
||||
}
|
||||
|
||||
result, _, err := prog.Eval(map[string]interface{}{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to evaluate expression %q: %v", tt.expression, err)
|
||||
}
|
||||
|
||||
if result != tt.expected {
|
||||
t.Errorf("%s: expected %v, got %v", tt.description, tt.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("function-compilation", func(t *testing.T) {
|
||||
src := `regexSafe(".*")`
|
||||
_, err := Compile(env, src)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to compile regexSafe expression: %v", err)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("dnsFunctions", func(t *testing.T) {
|
||||
originalDNSLookupAddr := dns.DNSLookupAddr
|
||||
originalDNSLookupHost := dns.DNSLookupHost
|
||||
defer func() {
|
||||
dns.DNSLookupAddr = originalDNSLookupAddr
|
||||
dns.DNSLookupHost = originalDNSLookupHost
|
||||
}()
|
||||
|
||||
t.Run("reverseDNS", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
addr string
|
||||
mockReturn []string
|
||||
mockError error
|
||||
expression string
|
||||
expected ref.Val
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
addr: "8.8.8.8",
|
||||
mockReturn: []string{"dns.google."},
|
||||
expression: `reverseDNS("8.8.8.8")`,
|
||||
expected: types.NewStringList(types.DefaultTypeAdapter, []string{"dns.google"}),
|
||||
description: "should return domain names for an IP",
|
||||
},
|
||||
{
|
||||
name: "not-found",
|
||||
addr: "127.0.0.1",
|
||||
mockReturn: []string{},
|
||||
mockError: &net.DNSError{IsNotFound: true},
|
||||
expression: `reverseDNS("127.0.0.1")`,
|
||||
expected: types.NewStringList(types.DefaultTypeAdapter, []string{}),
|
||||
description: "should return an empty list when not found",
|
||||
},
|
||||
{
|
||||
name: "error",
|
||||
addr: "error-addr",
|
||||
mockError: errors.New("some dns error"),
|
||||
expression: `reverseDNS("error-addr")`,
|
||||
expected: types.NewStringList(types.DefaultTypeAdapter, []string{}),
|
||||
description: "should return empty list on error",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
dns.DNSLookupAddr = func(addr string) ([]string, error) {
|
||||
if addr == tt.addr {
|
||||
return tt.mockReturn, tt.mockError
|
||||
}
|
||||
return nil, errors.New("unexpected address for reverse lookup")
|
||||
}
|
||||
|
||||
prog, err := Compile(env, tt.expression)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to compile expression %q: %v", tt.expression, err)
|
||||
}
|
||||
|
||||
result, _, err := prog.Eval(map[string]interface{}{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to evaluate expression %q: %v", tt.expression, err)
|
||||
}
|
||||
if result.Equal(tt.expected) != types.True {
|
||||
t.Errorf("%s: expected %v, got %v", tt.description, tt.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("lookupHost", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
host string
|
||||
mockReturn []string
|
||||
mockError error
|
||||
expression string
|
||||
expected ref.Val
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
host: "dns.google",
|
||||
mockReturn: []string{"8.8.8.8", "8.8.4.4"},
|
||||
expression: `lookupHost("dns.google")`,
|
||||
expected: types.NewStringList(types.DefaultTypeAdapter, []string{"8.8.8.8", "8.8.4.4"}),
|
||||
description: "should return IPs for a domain name",
|
||||
},
|
||||
{
|
||||
name: "not-found",
|
||||
host: "nonexistent.domain.example.com",
|
||||
mockReturn: []string{},
|
||||
mockError: &net.DNSError{IsNotFound: true},
|
||||
expression: `lookupHost("nonexistent.domain.example.com")`,
|
||||
expected: types.NewStringList(types.DefaultTypeAdapter, []string{}),
|
||||
description: "should return an empty list when not found",
|
||||
},
|
||||
{
|
||||
name: "error",
|
||||
host: "error-host",
|
||||
mockError: errors.New("some dns error"),
|
||||
expression: `lookupHost("error-host")`,
|
||||
expected: types.NewStringList(types.DefaultTypeAdapter, []string{}),
|
||||
description: "should return empty list on error",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
dns.DNSLookupHost = func(host string) ([]string, error) {
|
||||
if host == tt.host {
|
||||
return tt.mockReturn, tt.mockError
|
||||
}
|
||||
return nil, errors.New("unexpected host for forward lookup")
|
||||
}
|
||||
|
||||
prog, err := Compile(env, tt.expression)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to compile expression %q: %v", tt.expression, err)
|
||||
}
|
||||
|
||||
result, _, err := prog.Eval(map[string]interface{}{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to evaluate expression %q: %v", tt.expression, err)
|
||||
}
|
||||
if result.Equal(tt.expected) != types.True {
|
||||
t.Errorf("%s: expected %v, got %v", tt.description, tt.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("verifyFCrDNS", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
addr string
|
||||
reverseMockReturn []string
|
||||
reverseMockError error
|
||||
forwardMockReturn map[string][]string // name -> ips
|
||||
forwardMockError map[string]error
|
||||
expression string
|
||||
expected types.Bool
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
addr: "8.8.8.8",
|
||||
reverseMockReturn: []string{"dns.google."},
|
||||
forwardMockReturn: map[string][]string{"dns.google": {"8.8.8.8", "8.8.4.4"}},
|
||||
expression: `verifyFCrDNS("8.8.8.8")`,
|
||||
expected: types.Bool(true),
|
||||
description: "should return true for valid FCrDNS",
|
||||
},
|
||||
{
|
||||
name: "failure",
|
||||
addr: "1.2.3.4",
|
||||
reverseMockReturn: []string{"spoofed.example.com."},
|
||||
forwardMockReturn: map[string][]string{"spoofed.example.com": {"5.6.7.8"}},
|
||||
expression: `verifyFCrDNS("1.2.3.4")`,
|
||||
expected: types.Bool(false),
|
||||
description: "should return false for invalid FCrDNS",
|
||||
},
|
||||
{
|
||||
name: "reverse-lookup-fails",
|
||||
addr: "1.1.1.1",
|
||||
reverseMockError: errors.New("reverse lookup failed"),
|
||||
expression: `verifyFCrDNS("1.1.1.1")`,
|
||||
expected: types.Bool(false),
|
||||
description: "should return false if reverse lookup fails",
|
||||
},
|
||||
{
|
||||
name: "success-with-pattern",
|
||||
addr: "8.8.8.8",
|
||||
reverseMockReturn: []string{"dns.google."},
|
||||
forwardMockReturn: map[string][]string{"dns.google": {"8.8.8.8"}},
|
||||
expression: `verifyFCrDNS("8.8.8.8", "dns.google")`,
|
||||
expected: types.Bool(true),
|
||||
description: "should return true for valid FCrDNS with matching pattern",
|
||||
},
|
||||
{
|
||||
name: "failure-with-pattern",
|
||||
addr: "8.8.8.8",
|
||||
reverseMockReturn: []string{"dns.google."},
|
||||
forwardMockReturn: map[string][]string{"dns.google": {"8.8.8.8"}},
|
||||
expression: `verifyFCrDNS("8.8.8.8", "wrong.pattern")`,
|
||||
expected: types.Bool(false),
|
||||
description: "should return false for FCrDNS with non-matching pattern",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
dns.DNSLookupAddr = func(addr string) ([]string, error) {
|
||||
if addr == tt.addr {
|
||||
return tt.reverseMockReturn, tt.reverseMockError
|
||||
}
|
||||
return nil, errors.New("unexpected address for reverse lookup")
|
||||
}
|
||||
dns.DNSLookupHost = func(host string) ([]string, error) {
|
||||
host = strings.TrimSuffix(host, ".")
|
||||
if ips, ok := tt.forwardMockReturn[host]; ok {
|
||||
return ips, nil
|
||||
}
|
||||
if err, ok := tt.forwardMockError[host]; ok {
|
||||
return nil, err
|
||||
}
|
||||
return nil, &net.DNSError{IsNotFound: true}
|
||||
}
|
||||
|
||||
prog, err := Compile(env, tt.expression)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to compile expression %q: %v", tt.expression, err)
|
||||
}
|
||||
|
||||
result, _, err := prog.Eval(map[string]interface{}{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to evaluate expression %q: %v", tt.expression, err)
|
||||
}
|
||||
if result.Equal(tt.expected) != types.True {
|
||||
t.Errorf("%s: expected %v, got %v", tt.description, tt.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("arpaReverseIP", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
expression string
|
||||
expected types.String
|
||||
description string
|
||||
evalError bool
|
||||
}{
|
||||
{
|
||||
name: "ipv4",
|
||||
expression: `arpaReverseIP("1.2.3.4")`,
|
||||
expected: types.String("4.3.2.1"),
|
||||
description: "should correctly reverse an IPv4 address",
|
||||
},
|
||||
{
|
||||
name: "ipv6",
|
||||
expression: `arpaReverseIP("2001:db8::1")`,
|
||||
expected: types.String("1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2"),
|
||||
description: "should correctly reverse an IPv6 address",
|
||||
},
|
||||
{
|
||||
name: "ipv6-full",
|
||||
expression: `arpaReverseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334")`,
|
||||
expected: types.String("4.3.3.7.0.7.3.0.e.2.a.8.0.0.0.0.0.0.0.0.3.a.5.8.8.b.d.0.1.0.0.2"),
|
||||
description: "should correctly reverse a fully expanded IPv6 address",
|
||||
},
|
||||
{
|
||||
name: "ipv6-loopback",
|
||||
expression: `arpaReverseIP("::1")`,
|
||||
expected: types.String("1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0"),
|
||||
description: "should correctly reverse the IPv6 loopback address",
|
||||
},
|
||||
{
|
||||
name: "invalid-ip",
|
||||
expression: `arpaReverseIP("not-an-ip")`,
|
||||
evalError: true,
|
||||
description: "should error on an invalid IP",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
prog, err := Compile(env, tt.expression)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to compile expression %q: %v", tt.expression, err)
|
||||
}
|
||||
|
||||
result, _, err := prog.Eval(map[string]interface{}{})
|
||||
if tt.evalError {
|
||||
if err == nil {
|
||||
t.Errorf("%s: expected an evaluation error, but got none", tt.description)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("failed to evaluate expression %q: %v", tt.expression, err)
|
||||
}
|
||||
if result.Equal(tt.expected) != types.True {
|
||||
t.Errorf("%s: expected %v, got %v", tt.description, tt.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestThresholdEnvironment(t *testing.T) {
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/TecharoHQ/anubis/internal"
|
||||
"github.com/TecharoHQ/anubis/internal/dns"
|
||||
"github.com/TecharoHQ/anubis/lib/config"
|
||||
"github.com/TecharoHQ/anubis/lib/policy/checker"
|
||||
"github.com/TecharoHQ/anubis/lib/store"
|
||||
@@ -43,8 +42,6 @@ type ParsedConfig struct {
|
||||
StatusCodes config.StatusCodes
|
||||
DefaultDifficulty int
|
||||
DNSBL bool
|
||||
DnsCache *dns.DnsCache
|
||||
Dns *dns.Dns
|
||||
Logger *slog.Logger
|
||||
}
|
||||
|
||||
@@ -69,45 +66,6 @@ func ParseConfig(ctx context.Context, fin io.Reader, fname string, defaultDiffic
|
||||
result := newParsedConfig(c)
|
||||
result.DefaultDifficulty = defaultDifficulty
|
||||
|
||||
if c.Logging.Level != nil {
|
||||
logLevel = c.Logging.Level.String()
|
||||
}
|
||||
|
||||
switch c.Logging.Sink {
|
||||
case config.LogSinkStdio:
|
||||
result.Logger = internal.InitSlog(logLevel, os.Stderr)
|
||||
case config.LogSinkFile:
|
||||
out := &logrotate.Logger{
|
||||
Filename: c.Logging.Parameters.Filename,
|
||||
FilenameTimeFormat: time.RFC3339,
|
||||
MaxBytes: c.Logging.Parameters.MaxBytes,
|
||||
MaxAge: c.Logging.Parameters.MaxAge,
|
||||
MaxBackups: c.Logging.Parameters.MaxBackups,
|
||||
LocalTime: c.Logging.Parameters.UseLocalTime,
|
||||
Compress: c.Logging.Parameters.Compress,
|
||||
}
|
||||
|
||||
result.Logger = internal.InitSlog(logLevel, out)
|
||||
}
|
||||
|
||||
lg := result.Logger.With("at", "config-validate")
|
||||
|
||||
stFac, ok := store.Get(c.Store.Backend)
|
||||
switch ok {
|
||||
case true:
|
||||
store, err := stFac.Build(ctx, c.Store.Parameters)
|
||||
if err != nil {
|
||||
validationErrs = append(validationErrs, err)
|
||||
} else {
|
||||
result.Store = store
|
||||
}
|
||||
case false:
|
||||
validationErrs = append(validationErrs, config.ErrUnknownStoreBackend)
|
||||
}
|
||||
|
||||
result.DnsCache = dns.NewDNSCache(result.orig.DNSTTL.Forward, result.orig.DNSTTL.Reverse, result.Store)
|
||||
result.Dns = dns.New(ctx, result.DnsCache)
|
||||
|
||||
for _, b := range c.Bots {
|
||||
if berr := b.Valid(); berr != nil {
|
||||
validationErrs = append(validationErrs, berr)
|
||||
@@ -158,7 +116,7 @@ func ParseConfig(ctx context.Context, fin io.Reader, fname string, defaultDiffic
|
||||
}
|
||||
|
||||
if b.Expression != nil {
|
||||
c, err := NewCELChecker(b.Expression, result.Dns)
|
||||
c, err := NewCELChecker(b.Expression)
|
||||
if err != nil {
|
||||
validationErrs = append(validationErrs, fmt.Errorf("while processing rule %s expressions: %w", b.Name, err))
|
||||
} else {
|
||||
@@ -168,7 +126,7 @@ func ParseConfig(ctx context.Context, fin io.Reader, fname string, defaultDiffic
|
||||
|
||||
if b.ASNs != nil {
|
||||
if !hasThothClient {
|
||||
lg.Warn("You have specified a Thoth specific check but you have no Thoth client configured. Please read https://anubis.techaro.lol/docs/admin/thoth for more information", "check", "asn", "settings", b.ASNs)
|
||||
slog.Warn("You have specified a Thoth specific check but you have no Thoth client configured. Please read https://anubis.techaro.lol/docs/admin/thoth for more information", "check", "asn", "settings", b.ASNs)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -177,7 +135,7 @@ func ParseConfig(ctx context.Context, fin io.Reader, fname string, defaultDiffic
|
||||
|
||||
if b.GeoIP != nil {
|
||||
if !hasThothClient {
|
||||
lg.Warn("You have specified a Thoth specific check but you have no Thoth client configured. Please read https://anubis.techaro.lol/docs/admin/thoth for more information", "check", "geoip", "settings", b.GeoIP)
|
||||
slog.Warn("You have specified a Thoth specific check but you have no Thoth client configured. Please read https://anubis.techaro.lol/docs/admin/thoth for more information", "check", "geoip", "settings", b.GeoIP)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -187,6 +145,7 @@ func ParseConfig(ctx context.Context, fin io.Reader, fname string, defaultDiffic
|
||||
if b.Challenge == nil {
|
||||
parsedBot.Challenge = &config.ChallengeRules{
|
||||
Difficulty: defaultDifficulty,
|
||||
ReportAs: defaultDifficulty,
|
||||
Algorithm: "fast",
|
||||
}
|
||||
} else {
|
||||
@@ -196,7 +155,7 @@ func ParseConfig(ctx context.Context, fin io.Reader, fname string, defaultDiffic
|
||||
}
|
||||
|
||||
if parsedBot.Challenge.Algorithm == "slow" {
|
||||
lg.Warn("use of deprecated algorithm \"slow\" detected, please update this to \"fast\" when possible", "name", parsedBot.Name)
|
||||
slog.Warn("use of deprecated algorithm \"slow\" detected, please update this to \"fast\" when possible", "name", parsedBot.Name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,20 +172,17 @@ func ParseConfig(ctx context.Context, fin io.Reader, fname string, defaultDiffic
|
||||
|
||||
for _, t := range c.Thresholds {
|
||||
if t.Challenge != nil && t.Challenge.Algorithm == "slow" {
|
||||
lg.Warn("use of deprecated algorithm \"slow\" detected, please update this to \"fast\" when possible", "name", t.Name)
|
||||
}
|
||||
|
||||
if t.Challenge != nil && t.Challenge.ReportAs != 0 {
|
||||
lg.Warn("use of deprecated report_as setting detected, please remove this from your policy file when possible", "name", t.Name)
|
||||
slog.Warn("use of deprecated algorithm \"slow\" detected, please update this to \"fast\" when possible", "name", t.Name)
|
||||
}
|
||||
|
||||
if t.Name == "legacy-anubis-behaviour" && t.Expression.String() == "true" {
|
||||
if !warnedAboutThresholds.Load() {
|
||||
lg.Warn("configuration file does not contain thresholds, see docs for details on how to upgrade", "fname", fname, "docs_url", "https://anubis.techaro.lol/docs/admin/configuration/thresholds/")
|
||||
slog.Warn("configuration file does not contain thresholds, see docs for details on how to upgrade", "fname", fname, "docs_url", "https://anubis.techaro.lol/docs/admin/configuration/thresholds/")
|
||||
warnedAboutThresholds.Store(true)
|
||||
}
|
||||
|
||||
t.Challenge.Difficulty = defaultDifficulty
|
||||
t.Challenge.ReportAs = defaultDifficulty
|
||||
}
|
||||
|
||||
threshold, err := ParsedThresholdFromConfig(t)
|
||||
@@ -238,6 +194,40 @@ func ParseConfig(ctx context.Context, fin io.Reader, fname string, defaultDiffic
|
||||
result.Thresholds = append(result.Thresholds, threshold)
|
||||
}
|
||||
|
||||
stFac, ok := store.Get(c.Store.Backend)
|
||||
switch ok {
|
||||
case true:
|
||||
store, err := stFac.Build(ctx, c.Store.Parameters)
|
||||
if err != nil {
|
||||
validationErrs = append(validationErrs, err)
|
||||
} else {
|
||||
result.Store = store
|
||||
}
|
||||
case false:
|
||||
validationErrs = append(validationErrs, config.ErrUnknownStoreBackend)
|
||||
}
|
||||
|
||||
if c.Logging.Level != nil {
|
||||
logLevel = c.Logging.Level.String()
|
||||
}
|
||||
|
||||
switch c.Logging.Sink {
|
||||
case config.LogSinkStdio:
|
||||
result.Logger = internal.InitSlog(logLevel, os.Stderr)
|
||||
case config.LogSinkFile:
|
||||
out := &logrotate.Logger{
|
||||
Filename: c.Logging.Parameters.Filename,
|
||||
FilenameTimeFormat: time.RFC3339,
|
||||
MaxBytes: c.Logging.Parameters.MaxBytes,
|
||||
MaxAge: c.Logging.Parameters.MaxAge,
|
||||
MaxBackups: c.Logging.Parameters.MaxBackups,
|
||||
LocalTime: c.Logging.Parameters.UseLocalTime,
|
||||
Compress: c.Logging.Parameters.Compress,
|
||||
}
|
||||
|
||||
result.Logger = internal.InitSlog(logLevel, out)
|
||||
}
|
||||
|
||||
if len(validationErrs) > 0 {
|
||||
return nil, fmt.Errorf("errors validating policy config JSON %s: %w", fname, errors.Join(validationErrs...))
|
||||
}
|
||||
|
||||
1
lib/testdata/invalid-challenge-method.yaml
vendored
1
lib/testdata/invalid-challenge-method.yaml
vendored
@@ -4,4 +4,5 @@ bots:
|
||||
action: CHALLENGE
|
||||
challenge:
|
||||
difficulty: 16
|
||||
report_as: 4
|
||||
algorithm: hunter2 # invalid algorithm
|
||||
|
||||
1
lib/testdata/test_config.yaml
vendored
1
lib/testdata/test_config.yaml
vendored
@@ -42,3 +42,4 @@ thresholds:
|
||||
challenge:
|
||||
algorithm: fast
|
||||
difficulty: 1
|
||||
report_as: 1
|
||||
|
||||
1
lib/testdata/zero_difficulty.yaml
vendored
1
lib/testdata/zero_difficulty.yaml
vendored
@@ -42,3 +42,4 @@ thresholds:
|
||||
challenge:
|
||||
algorithm: fast
|
||||
difficulty: 0
|
||||
report_as: 0
|
||||
|
||||
7
package-lock.json
generated
7
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@techaro/anubis",
|
||||
"version": "1.24.0-pre1",
|
||||
"version": "1.23.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@techaro/anubis",
|
||||
"version": "1.24.0-pre1",
|
||||
"version": "1.23.1",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@aws-crypto/sha256-js": "^5.2.0",
|
||||
@@ -712,7 +712,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.8.19",
|
||||
"caniuse-lite": "^1.0.30001751",
|
||||
@@ -1518,6 +1517,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
@@ -2611,7 +2611,6 @@
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@techaro/anubis",
|
||||
"version": "1.24.0-pre1",
|
||||
"version": "1.23.1",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
41
test/go.mod
41
test/go.mod
@@ -18,24 +18,23 @@ require (
|
||||
github.com/TecharoHQ/thoth-proto v0.5.0 // indirect
|
||||
github.com/a-h/templ v0.3.960 // indirect
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.40.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.39.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.31.20 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.24 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.92.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.40.2 // indirect
|
||||
github.com/aws/smithy-go v1.23.2 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
@@ -74,18 +73,18 @@ require (
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.67.2 // indirect
|
||||
github.com/prometheus/procfs v0.19.2 // indirect
|
||||
github.com/redis/go-redis/v9 v9.17.0 // indirect
|
||||
github.com/redis/go-redis/v9 v9.16.0 // indirect
|
||||
github.com/sebest/xff v0.0.0-20210106013422-671bd2870b3a // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.25.10 // indirect
|
||||
github.com/stoewer/go-strcase v1.3.1 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.etcd.io/bbolt v1.4.3 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect
|
||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.37.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
@@ -93,7 +92,7 @@ require (
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba // indirect
|
||||
google.golang.org/grpc v1.77.0 // indirect
|
||||
google.golang.org/grpc v1.76.0 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
gotest.tools/v3 v3.5.2 // indirect
|
||||
k8s.io/apimachinery v0.34.2 // indirect
|
||||
|
||||
94
test/go.sum
94
test/go.sum
@@ -16,42 +16,40 @@ github.com/a-h/templ v0.3.960 h1:trshEpGa8clF5cdI39iY4ZrZG8Z/QixyzEyUnA7feTM=
|
||||
github.com/a-h/templ v0.3.960/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
|
||||
github.com/aws/aws-sdk-go-v2 v1.40.0 h1:/WMUA0kjhZExjOQN2z3oLALDREea1A7TobfuiBrKlwc=
|
||||
github.com/aws/aws-sdk-go-v2 v1.40.0/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE=
|
||||
github.com/aws/aws-sdk-go-v2 v1.39.6 h1:2JrPCVgWJm7bm83BDwY5z8ietmeJUbh3O2ACnn+Xsqk=
|
||||
github.com/aws/aws-sdk-go-v2 v1.39.6/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3 h1:DHctwEM8P8iTXFxC/QK0MRjwEpWQeM9yzidCRjldUz0=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3/go.mod h1:xdCzcZEtnSTKVDOmUZs4l/j3pSV6rpo1WXl5ugNsL8Y=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.1 h1:iODUDLgk3q8/flEC7ymhmxjfoAnBDwEEYEVyKZ9mzjU=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.1/go.mod h1:xoAgo17AGrPpJBSLg81W+ikM0cpOZG8ad04T2r+d5P0=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.1 h1:JeW+EwmtTE0yXFK8SmklrFh/cGTTXsQJumgMZNlbxfM=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.1/go.mod h1:BOoXiStwTF+fT2XufhO0Efssbi1CNIO/ZXpZu87N0pw=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14 h1:WZVR5DbDgxzA0BJeudId89Kmgy6DIU4ORpxwsVHz0qA=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14/go.mod h1:Dadl9QO0kHgbrH1GRqGiZdYtW5w+IXXaBNCHTIaheM4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14 h1:PZHqQACxYb8mYgms4RZbhZG0a7dPW06xOjmaH0EJC/I=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14/go.mod h1:VymhrMJUWs69D8u0/lZ7jSB6WgaG/NqHi3gX0aYf6U0=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14 h1:bOS19y6zlJwagBfHxs0ESzr1XCOU2KXJCWcq3E2vfjY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14/go.mod h1:1ipeGBMAxZ0xcTm6y6paC2C/J6f6OO7LBODV9afuAyM=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.31.20 h1:/jWF4Wu90EhKCgjTdy1DGxcbcbNrjfBHvksEL79tfQc=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.31.20/go.mod h1:95Hh1Tc5VYKL9NJ7tAkDcqeKt+MCXQB1hQZaRdJIZE0=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.24 h1:iJ2FmPT35EaIB0+kMa6TnQ+PwG5A1prEdAw+PsMzfHg=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.24/go.mod h1:U91+DrfjAiXPDEGYhh/x29o4p0qHX5HDqG7y5VViv64=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 h1:T1brd5dR3/fzNFAQch/iBKeX07/ffu/cLu+q+RuzEWk=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13/go.mod h1:Peg/GBAQ6JDt+RoBf4meB1wylmAipb7Kg2ZFakZTlwk=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 h1:a+8/MLcWlIxo1lF9xaGt3J/u3yOZx+CdSveSNwjhD40=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13/go.mod h1:oGnKwIYZ4XttyU2JWxFrwvhF6YKiK/9/wmE3v3Iu9K8=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 h1:HBSI2kDkMdWz4ZM7FjwE7e/pWDEZ+nR95x8Ztet1ooY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13/go.mod h1:YE94ZoDArI7awZqJzBAZ3PDD2zSfuP7w6P2knOzIn8M=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.14 h1:ITi7qiDSv/mSGDSWNpZ4k4Ve0DQR6Ug2SJQ8zEHoDXg=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.14/go.mod h1:k1xtME53H1b6YpZt74YmwlONMWf4ecM+lut1WQLAF/U=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13 h1:eg/WYAa12vqTphzIdWMzqYRVKKnCboVPRlvaybNCqPA=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13/go.mod h1:/FDdxWhz1486obGrKKC1HONd7krpk38LBt+dutLcN9k=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 h1:x2Ibm/Af8Fi+BH+Hsn9TXGdT+hKbDd5XOTZxTMxDk7o=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3/go.mod h1:IW1jwyrQgMdhisceG8fQLmQIydcT/jWY21rFhzgaKwo=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.5 h1:Hjkh7kE6D81PgrHlE/m9gx+4TyyeLHuY8xJs7yXN5C4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.5/go.mod h1:nPRXgyCfAurhyaTMoBMwRBYBhaHI4lNPAnJmjM0Tslc=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14 h1:FIouAnCE46kyYqyhs0XEBDFFSREtdnr8HQuLPQPLCrY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14/go.mod h1:UTwDc5COa5+guonQU8qBikJo1ZJ4ln2r1MkF7Dqag1E=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.14 h1:FzQE21lNtUor0Fb7QNgnEyiRCBlolLTX/Z1j65S7teM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.14/go.mod h1:s1ydyWG9pm3ZwmmYN21HKyG9WzAZhYVW85wMHs5FV6w=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.92.0 h1:8FshVvnV2sr9kOSAbOnc/vwVmmAwMjOedKH6JW2ddPM=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.92.0/go.mod h1:wYNqY3L02Z3IgRYxOBPH9I1zD9Cjh9hI5QOy/eOjQvw=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.1 h1:BDgIUYGEo5TkayOWv/oBLPphWwNm/A91AebUjAu5L5g=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.1/go.mod h1:iS6EPmNeqCsGo+xQmXv0jIMjyYtQfnwg36zl2FwEouk=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.4 h1:U//SlnkE1wOQiIImxzdY5PXat4Wq+8rlfVEw4Y7J8as=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.4/go.mod h1:av+ArJpoYf3pgyrj6tcehSFW+y9/QvAY8kMooR9bZCw=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.9 h1:LU8S9W/mPDAU9q0FjCLi0TrCheLMGwzbRpvUMwYspcA=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.9/go.mod h1:/j67Z5XBVDx8nZVp9EuFM9/BS5dvBznbqILGuu73hug=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.1 h1:GdGmKtG+/Krag7VfyOXV17xjTCz0i9NT+JnqLTOI5nA=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.1/go.mod h1:6TxbXoDSgBQ225Qd8Q+MbxUxUh6TtNKwbRt/EPS9xso=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4 h1:NvMjwvv8hpGUILarKw7Z4Q0w1H9anXKsesMxtw++MA4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4/go.mod h1:455WPHSwaGj2waRSpQp7TsnpOnBfw8iDfPfbwl7KPJE=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 h1:kDqdFvMY4AtKoACfzIGD8A0+hbT41KTKF//gq7jITfM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13/go.mod h1:lmKuogqSU3HzQCwZ9ZtcqOc5XGMqtDK7OIc2+DxiUEg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13 h1:zhBJXdhWIFZ1acfDYIhu4+LCzdUS2Vbcum7D01dXlHQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13/go.mod h1:JaaOeCE368qn2Hzi3sEzY6FgAZVCIYcC2nwbro2QCh8=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.2 h1:DhdbtDl4FdNlj31+xiRXANxEE+eC7n8JQz+/ilwQ8Uc=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.2/go.mod h1:+wArOOrcHUevqdto9k1tKOF5++YTe9JEcPSc9Tx2ZSw=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.3 h1:NjShtS1t8r5LUfFVtFeI8xLAHQNTa7UI0VawXlrBMFQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.3/go.mod h1:fKvyjJcz63iL/ftA6RaM8sRCtN4r4zl4tjL3qw5ec7k=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.7 h1:gTsnx0xXNQ6SBbymoDvcoRHL+q4l/dAFsQuKfDWSaGc=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.7/go.mod h1:klO+ejMvYsB4QATfEOIXk8WAEwN4N0aBfJpvC+5SZBo=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.40.2 h1:HK5ON3KmQV2HcAunnx4sKLB9aPf3gKGwVAf7xnx0QT0=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.40.2/go.mod h1:E19xDjpzPZC7LS2knI9E6BaRFDK43Eul7vd6rSq2HWk=
|
||||
github.com/aws/smithy-go v1.23.2 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM=
|
||||
github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
@@ -191,10 +189,10 @@ github.com/prometheus/common v0.67.2 h1:PcBAckGFTIHt2+L3I33uNRTlKTplNzFctXcWhPyA
|
||||
github.com/prometheus/common v0.67.2/go.mod h1:63W3KZb1JOKgcjlIr64WW/LvFGAqKPj0atm+knVGEko=
|
||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
||||
github.com/redis/go-redis/v9 v9.17.0 h1:K6E+ZlYN95KSMmZeEQPbU/c++wfmEvfFB17yEAq/VhM=
|
||||
github.com/redis/go-redis/v9 v9.17.0/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/redis/go-redis/v9 v9.16.0 h1:OotgqgLSRCmzfqChbQyG1PHC3tLNR89DG4jdOERSEP4=
|
||||
github.com/redis/go-redis/v9 v9.16.0/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/sebest/xff v0.0.0-20210106013422-671bd2870b3a h1:iLcLb5Fwwz7g/DLK89F+uQBDeAhHhwdzB5fSlVdhGcM=
|
||||
github.com/sebest/xff v0.0.0-20210106013422-671bd2870b3a/go.mod h1:wozgYq9WEBQBaIJe4YZ0qTSFAMxmcwBhQH0fO0R34Z0=
|
||||
github.com/shirou/gopsutil/v4 v4.25.10 h1:at8lk/5T1OgtuCp+AwrDofFRjnvosn0nkN2OLQ6g8tA=
|
||||
@@ -221,24 +219,24 @@ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
|
||||
go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
|
||||
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 h1:bDMKF3RUSxshZ5OjOTi8rsHGaPKsAt76FaqgvIUySLc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0/go.mod h1:dDT67G/IkA46Mr2l9Uj7HsQVwsjASyV9SjGofsiUZDA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
|
||||
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
|
||||
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
|
||||
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
|
||||
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
|
||||
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
||||
go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os=
|
||||
go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
@@ -271,8 +269,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba h1:
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:G5IanEx8/PgI9w6CFcYQf7jMtHQhZruvfM1i3qOqk5U=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba h1:UKgtfRM7Yh93Sya0Fo8ZzhDP4qBckrrxEr2oF5UIVb8=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
|
||||
google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
|
||||
google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
|
||||
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -4,6 +4,7 @@ bots:
|
||||
action: CHALLENGE
|
||||
challenge:
|
||||
difficulty: 2
|
||||
report_as: 2
|
||||
algorithm: fast
|
||||
|
||||
status_codes:
|
||||
|
||||
@@ -155,7 +155,7 @@ const t = (key) => translations[`js_${key}`] || translations[key] || key;
|
||||
return;
|
||||
}
|
||||
|
||||
status.innerHTML = `${t('calculating_difficulty')} ${rules.difficulty}, `;
|
||||
status.innerHTML = `${t('calculating_difficulty')} ${rules.report_as}, `;
|
||||
progress.style.display = "inline-block";
|
||||
|
||||
// the whole text, including "Speed:", as a single node, because some browsers
|
||||
@@ -166,7 +166,7 @@ const t = (key) => translations[`js_${key}`] || translations[key] || key;
|
||||
|
||||
let lastSpeedUpdate = 0;
|
||||
let showingApology = false;
|
||||
const likelihood = Math.pow(16, -rules.difficulty);
|
||||
const likelihood = Math.pow(16, -rules.report_as);
|
||||
|
||||
try {
|
||||
const t0 = Date.now();
|
||||
|
||||
Reference in New Issue
Block a user