mirror of
https://github.com/TecharoHQ/anubis.git
synced 2026-04-05 16:28:17 +00:00
Compare commits
3 Commits
Xe/test-xf
...
Xe/docs-re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
617812a039 | ||
|
|
9ddbc69c0e | ||
|
|
edb7ab1ae9 |
@@ -2,12 +2,10 @@
|
||||
// README at: https://github.com/devcontainers/templates/tree/main/src/debian
|
||||
{
|
||||
"name": "Dev",
|
||||
"dockerComposeFile": [
|
||||
"./docker-compose.yaml"
|
||||
],
|
||||
"dockerComposeFile": ["./docker-compose.yaml"],
|
||||
"service": "workspace",
|
||||
"workspaceFolder": "/workspace/anubis",
|
||||
"postStartCommand": "bash ./.devcontainer/poststart.sh",
|
||||
"postStartCommand": "npm ci && go mod download",
|
||||
"features": {
|
||||
"ghcr.io/xe/devcontainer-features/ko:1.1.0": {},
|
||||
"ghcr.io/devcontainers/features/github-cli:1": {}
|
||||
@@ -21,9 +19,8 @@
|
||||
"golang.go",
|
||||
"unifiedjs.vscode-mdx",
|
||||
"a-h.templ",
|
||||
"redhat.vscode-yaml",
|
||||
"streetsidesoftware.code-spell-checker"
|
||||
"redhat.vscode-yaml"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
pwd
|
||||
|
||||
npm ci &
|
||||
go mod download &
|
||||
go install ./utils/cmd/... &
|
||||
|
||||
wait
|
||||
2
.github/actions/spelling/allow.txt
vendored
2
.github/actions/spelling/allow.txt
vendored
@@ -3,5 +3,3 @@ https
|
||||
ssh
|
||||
ubuntu
|
||||
workarounds
|
||||
rjack
|
||||
msgbox
|
||||
1
.github/actions/spelling/excludes.txt
vendored
1
.github/actions/spelling/excludes.txt
vendored
@@ -92,4 +92,3 @@ ignore$
|
||||
robots.txt
|
||||
^lib/localization/locales/.*\.json$
|
||||
^lib/localization/.*_test.go$
|
||||
^test/.*$
|
||||
|
||||
30
.github/actions/spelling/expect.txt
vendored
30
.github/actions/spelling/expect.txt
vendored
@@ -1,10 +1,12 @@
|
||||
acs
|
||||
aeacus
|
||||
Aibrew
|
||||
alrest
|
||||
amazonbot
|
||||
anthro
|
||||
anubis
|
||||
anubistest
|
||||
apk
|
||||
Applebot
|
||||
archlinux
|
||||
asnc
|
||||
@@ -23,13 +25,11 @@ bitrate
|
||||
Bluesky
|
||||
blueskybot
|
||||
boi
|
||||
Bokm
|
||||
botnet
|
||||
botstopper
|
||||
BPort
|
||||
Brightbot
|
||||
broked
|
||||
byteslice
|
||||
Bytespider
|
||||
cachebuster
|
||||
cachediptoasn
|
||||
@@ -60,6 +60,7 @@ connnection
|
||||
containerbuild
|
||||
coreutils
|
||||
Cotoyogi
|
||||
CRDs
|
||||
Cromite
|
||||
crt
|
||||
Cscript
|
||||
@@ -118,7 +119,6 @@ goland
|
||||
gomod
|
||||
goodbot
|
||||
googlebot
|
||||
gopsutil
|
||||
govulncheck
|
||||
goyaml
|
||||
GPG
|
||||
@@ -130,7 +130,6 @@ Hashcash
|
||||
hashrate
|
||||
headermap
|
||||
healthcheck
|
||||
healthz
|
||||
hec
|
||||
hmc
|
||||
hostable
|
||||
@@ -146,10 +145,8 @@ Imagesift
|
||||
imgproxy
|
||||
impressum
|
||||
inp
|
||||
internets
|
||||
IPTo
|
||||
iptoasn
|
||||
isp
|
||||
iss
|
||||
isset
|
||||
ivh
|
||||
@@ -161,6 +158,7 @@ jshelter
|
||||
JWTs
|
||||
kagi
|
||||
kagibot
|
||||
keikaku
|
||||
Keyfunc
|
||||
keypair
|
||||
KHTML
|
||||
@@ -174,13 +172,13 @@ lgbt
|
||||
licend
|
||||
licstart
|
||||
lightpanda
|
||||
limsa
|
||||
LIMSA
|
||||
Linting
|
||||
linuxbrew
|
||||
LLU
|
||||
loadbalancer
|
||||
lol
|
||||
lominsa
|
||||
LOMINSA
|
||||
maintainership
|
||||
malware
|
||||
mcr
|
||||
@@ -188,13 +186,12 @@ memes
|
||||
metarefresh
|
||||
metrix
|
||||
mimi
|
||||
Minfilia
|
||||
minica
|
||||
mistralai
|
||||
Mojeek
|
||||
mojeekbot
|
||||
mozilla
|
||||
nbf
|
||||
nepeat
|
||||
netsurf
|
||||
nginx
|
||||
nicksnyder
|
||||
@@ -223,7 +220,6 @@ pipefail
|
||||
pki
|
||||
podkova
|
||||
podman
|
||||
poststart
|
||||
prebaked
|
||||
privkey
|
||||
promauto
|
||||
@@ -243,24 +239,25 @@ redhat
|
||||
redir
|
||||
redirectscheme
|
||||
refactors
|
||||
relayd
|
||||
reputational
|
||||
reqmeta
|
||||
risc
|
||||
ruleset
|
||||
runlevels
|
||||
RUnlock
|
||||
runtimedir
|
||||
runtimedirectory
|
||||
sas
|
||||
sasl
|
||||
searchbot
|
||||
searx
|
||||
sebest
|
||||
secretplans
|
||||
selfsigned
|
||||
Semrush
|
||||
Seo
|
||||
setsebool
|
||||
shellcheck
|
||||
shirou
|
||||
Sidetrade
|
||||
simprint
|
||||
sitemap
|
||||
@@ -298,8 +295,10 @@ uberspace
|
||||
Unbreak
|
||||
unbreakdocker
|
||||
unifiedjs
|
||||
unixhttpd
|
||||
unmarshal
|
||||
unparseable
|
||||
uuidgen
|
||||
uvx
|
||||
UXP
|
||||
valkey
|
||||
@@ -308,8 +307,6 @@ Velen
|
||||
vendored
|
||||
vhosts
|
||||
videotest
|
||||
VKE
|
||||
Vultr
|
||||
waitloop
|
||||
weblate
|
||||
webmaster
|
||||
@@ -319,11 +316,11 @@ websites
|
||||
Webzio
|
||||
wildbase
|
||||
withthothmock
|
||||
wolfbeast
|
||||
wordpress
|
||||
Workaround
|
||||
workdir
|
||||
wpbot
|
||||
xcaddy
|
||||
Xeact
|
||||
xeiaso
|
||||
xeserv
|
||||
@@ -340,7 +337,6 @@ yeet
|
||||
yeetfile
|
||||
yourdomain
|
||||
yoursite
|
||||
yyz
|
||||
Zenos
|
||||
zizmor
|
||||
zombocom
|
||||
|
||||
4
.github/actions/spelling/patterns.txt
vendored
4
.github/actions/spelling/patterns.txt
vendored
@@ -132,7 +132,3 @@ go install(?:\s+[a-z]+\.[-@\w/.]+)+
|
||||
# hit-count: 1 file-count: 1
|
||||
# microsoft
|
||||
\b(?:https?://|)(?:(?:(?:blogs|download\.visualstudio|docs|msdn2?|research)\.|)microsoft|blogs\.msdn)\.co(?:m|\.\w\w)/[-_a-zA-Z0-9()=./%]*
|
||||
|
||||
# hit-count: 1 file-count: 1
|
||||
# data url
|
||||
\bdata:[-a-zA-Z=;:/0-9+]*,\S*
|
||||
11
.github/workflows/docs-deploy.yml
vendored
11
.github/workflows/docs-deploy.yml
vendored
@@ -36,9 +36,6 @@ jobs:
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||
with:
|
||||
images: ghcr.io/techarohq/anubis/docs
|
||||
tags: |
|
||||
type=sha,enable=true,priority=100,prefix=,suffix=,format=long
|
||||
main
|
||||
|
||||
- name: Build and push
|
||||
id: build
|
||||
@@ -52,15 +49,15 @@ jobs:
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
|
||||
- name: Apply k8s manifests to limsa lominsa
|
||||
uses: actions-hub/kubectl@b5b19eeb6a0ffde16637e398f8b96ef01eb8fdb7 # v1.33.3
|
||||
- name: Apply k8s manifests to aeacus
|
||||
uses: actions-hub/kubectl@d50394b7d704525f93faefce1e65a6329ff67271 # v1.33.2
|
||||
env:
|
||||
KUBE_CONFIG: ${{ secrets.LIMSA_LOMINSA_KUBECONFIG }}
|
||||
with:
|
||||
args: apply -k docs/manifest
|
||||
|
||||
- name: Apply k8s manifests to limsa lominsa
|
||||
uses: actions-hub/kubectl@b5b19eeb6a0ffde16637e398f8b96ef01eb8fdb7 # v1.33.3
|
||||
- name: Apply k8s manifests to aeacus
|
||||
uses: actions-hub/kubectl@d50394b7d704525f93faefce1e65a6329ff67271 # v1.33.2
|
||||
env:
|
||||
KUBE_CONFIG: ${{ secrets.LIMSA_LOMINSA_KUBECONFIG }}
|
||||
with:
|
||||
|
||||
7
.github/workflows/docs-test.yml
vendored
7
.github/workflows/docs-test.yml
vendored
@@ -2,7 +2,7 @@ name: Docs test build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
branches: [ "main" ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -24,10 +24,7 @@ jobs:
|
||||
id: meta
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||
with:
|
||||
images: ghcr.io/techarohq/anubis/docs
|
||||
tags: |
|
||||
type=sha,enable=true,priority=100,prefix=,suffix=,format=long
|
||||
main
|
||||
images: ghcr.io/${{ github.repository }}/docs
|
||||
|
||||
- name: Build and push
|
||||
id: build
|
||||
|
||||
2
.github/workflows/go.yml
vendored
2
.github/workflows/go.yml
vendored
@@ -82,7 +82,7 @@ jobs:
|
||||
run: npm run test
|
||||
|
||||
- name: Lint with staticcheck
|
||||
uses: dominikh/staticcheck-action@024238d2898c874f26d723e7d0ff4308c35589a2 # v1.4.0
|
||||
uses: dominikh/staticcheck-action@fe1dd0c3658873b46f8c9bb3291096a617310ca6 # v1.3.1
|
||||
with:
|
||||
version: "latest"
|
||||
|
||||
|
||||
117
.github/workflows/package-builds-stable.yml
vendored
117
.github/workflows/package-builds-stable.yml
vendored
@@ -1,9 +1,8 @@
|
||||
name: Package builds (stable)
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
# release:
|
||||
# types: [published]
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -14,67 +13,67 @@ jobs:
|
||||
#runs-on: alrest-techarohq
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-tags: true
|
||||
fetch-depth: 0
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-tags: true
|
||||
fetch-depth: 0
|
||||
|
||||
- name: build essential
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential
|
||||
- name: build essential
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential
|
||||
|
||||
- name: Set up Homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@main
|
||||
- name: Set up Homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@main
|
||||
|
||||
- name: Setup Homebrew cellar cache
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
with:
|
||||
path: |
|
||||
/home/linuxbrew/.linuxbrew/Cellar
|
||||
/home/linuxbrew/.linuxbrew/bin
|
||||
/home/linuxbrew/.linuxbrew/etc
|
||||
/home/linuxbrew/.linuxbrew/include
|
||||
/home/linuxbrew/.linuxbrew/lib
|
||||
/home/linuxbrew/.linuxbrew/opt
|
||||
/home/linuxbrew/.linuxbrew/sbin
|
||||
/home/linuxbrew/.linuxbrew/share
|
||||
/home/linuxbrew/.linuxbrew/var
|
||||
key: ${{ runner.os }}-go-homebrew-cellar-${{ hashFiles('go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-homebrew-cellar-
|
||||
- name: Setup Homebrew cellar cache
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
with:
|
||||
path: |
|
||||
/home/linuxbrew/.linuxbrew/Cellar
|
||||
/home/linuxbrew/.linuxbrew/bin
|
||||
/home/linuxbrew/.linuxbrew/etc
|
||||
/home/linuxbrew/.linuxbrew/include
|
||||
/home/linuxbrew/.linuxbrew/lib
|
||||
/home/linuxbrew/.linuxbrew/opt
|
||||
/home/linuxbrew/.linuxbrew/sbin
|
||||
/home/linuxbrew/.linuxbrew/share
|
||||
/home/linuxbrew/.linuxbrew/var
|
||||
key: ${{ runner.os }}-go-homebrew-cellar-${{ hashFiles('go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-homebrew-cellar-
|
||||
|
||||
- name: Install Brew dependencies
|
||||
run: |
|
||||
brew bundle
|
||||
- name: Install Brew dependencies
|
||||
run: |
|
||||
brew bundle
|
||||
|
||||
- name: Setup Golang caches
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{ runner.os }}-golang-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-golang-
|
||||
- name: Setup Golang caches
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{ runner.os }}-golang-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-golang-
|
||||
|
||||
- name: install node deps
|
||||
run: |
|
||||
npm ci
|
||||
- name: install node deps
|
||||
run: |
|
||||
npm ci
|
||||
|
||||
- name: Build Packages
|
||||
run: |
|
||||
go tool yeet
|
||||
- name: Build Packages
|
||||
run: |
|
||||
go tool yeet
|
||||
|
||||
- name: Upload released artifacts
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.TOKEN }}
|
||||
RELEASE_VERSION: ${{github.event.release.tag_name}}
|
||||
shell: bash
|
||||
run: |
|
||||
RELEASE="${RELEASE_VERSION}"
|
||||
cd var
|
||||
for file in *; do
|
||||
gh release upload $RELEASE $file
|
||||
done
|
||||
- name: Upload released artifacts
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.TOKEN }}
|
||||
RELEASE_VERSION: ${{github.event.release.tag_name}}
|
||||
shell: bash
|
||||
run: |
|
||||
RELEASE="${RELEASE_VERSION}"
|
||||
cd var
|
||||
for file in *; do
|
||||
gh release upload $RELEASE $file
|
||||
done
|
||||
|
||||
45
.github/workflows/smoke-tests.yml
vendored
45
.github/workflows/smoke-tests.yml
vendored
@@ -1,45 +0,0 @@
|
||||
name: Smoke tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
smoke-test:
|
||||
strategy:
|
||||
matrix:
|
||||
test:
|
||||
- git-clone
|
||||
- git-push
|
||||
- healthcheck
|
||||
- i18n
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version: latest
|
||||
|
||||
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: stable
|
||||
|
||||
- uses: ko-build/setup-ko@d006021bd0c28d1ce33a07e7943d48b079944c8d # v0.9
|
||||
|
||||
- name: Install utils
|
||||
run: |
|
||||
go install ./utils/cmd/...
|
||||
|
||||
- name: Run test
|
||||
run: |
|
||||
cd test/${{ matrix.test }}
|
||||
backoff-retry --try-count 10 ./test.sh
|
||||
8
.github/workflows/ssh-ci.yml
vendored
8
.github/workflows/ssh-ci.yml
vendored
@@ -25,19 +25,13 @@ jobs:
|
||||
fetch-tags: true
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install CI target SSH key
|
||||
uses: shimataro/ssh-key-action@d4fffb50872869abe2d9a9098a6d9c5aa7d16be4 # v2.7.0
|
||||
with:
|
||||
key: ${{ secrets.CI_SSH_KEY }}
|
||||
name: id_rsa
|
||||
known_hosts: ${{ secrets.CI_SSH_KNOWN_HOSTS }}
|
||||
|
||||
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: stable
|
||||
|
||||
- name: Run CI
|
||||
run: go run ./utils/cmd/backoff-retry bash test/ssh-ci/rigging.sh ${{ matrix.host }}
|
||||
run: bash test/ssh-ci/rigging.sh ${{ matrix.host }}
|
||||
env:
|
||||
GITHUB_RUN_ID: ${{ github.run_id }}
|
||||
|
||||
4
.github/workflows/zizmor.yml
vendored
4
.github/workflows/zizmor.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@7edac99f961f18b581bbd960d59d049f04c0002f # v6.4.1
|
||||
uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v6.3.1
|
||||
|
||||
- name: Run zizmor 🌈
|
||||
run: uvx zizmor --format sarif . > results.sarif
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Upload SARIF file
|
||||
uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||
uses: github/codeql-action/upload-sarif@39edc492dbe16b1465b0cafca41432d857bdb31a # v3.29.1
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
category: zizmor
|
||||
|
||||
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
@@ -5,7 +5,6 @@
|
||||
"golang.go",
|
||||
"unifiedjs.vscode-mdx",
|
||||
"a-h.templ",
|
||||
"redhat.vscode-yaml",
|
||||
"streetsidesoftware.code-spell-checker"
|
||||
"redhat.vscode-yaml"
|
||||
]
|
||||
}
|
||||
14
README.md
14
README.md
@@ -41,20 +41,6 @@ Anubis is brought to you by sponsors and donors like:
|
||||
<a href="https://wildbase.xyz/">
|
||||
<img src="./docs/static/img/sponsors/wildbase-logo.webp" alt="Wildbase" height="64">
|
||||
</a>
|
||||
<a href="https://emma.pet">
|
||||
<img
|
||||
src="./docs/static/img/sponsors/nepeat-logo.webp"
|
||||
alt="Cat eyes over the word Emma in a serif font"
|
||||
height="64"
|
||||
/>
|
||||
</a>
|
||||
<a href="https://fabulous.systems/">
|
||||
<img
|
||||
src="./docs/static/img/sponsors/fabulous-systems.webp"
|
||||
alt="Cat eyes over the word Emma in a serif font"
|
||||
height="64"
|
||||
/>
|
||||
</a>
|
||||
|
||||
## Overview
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ import (
|
||||
"github.com/facebookgo/flagenv"
|
||||
_ "github.com/joho/godotenv/autoload"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
healthv1 "google.golang.org/grpc/health/grpc_health_v1"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -98,7 +97,7 @@ func keyFromHex(value string) (ed25519.PrivateKey, error) {
|
||||
}
|
||||
|
||||
func doHealthCheck() error {
|
||||
resp, err := http.Get("http://localhost" + *metricsBind + "/healthz")
|
||||
resp, err := http.Get("http://localhost" + *metricsBind + anubis.BasePrefix + "/metrics")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch metrics: %w", err)
|
||||
}
|
||||
@@ -242,15 +241,6 @@ func main() {
|
||||
}
|
||||
|
||||
internal.InitSlog(*slogLevel)
|
||||
internal.SetHealth("anubis", healthv1.HealthCheckResponse_NOT_SERVING)
|
||||
|
||||
if *healthcheck {
|
||||
log.Println("running healthcheck")
|
||||
if err := doHealthCheck(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if *extractResources != "" {
|
||||
if err := extractEmbedFS(data.BotPolicies, ".", *extractResources); err != nil {
|
||||
@@ -263,17 +253,6 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
// install signal handler
|
||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
wg := new(sync.WaitGroup)
|
||||
|
||||
if *metricsBind != "" {
|
||||
wg.Add(1)
|
||||
go metricsServer(ctx, wg.Done)
|
||||
}
|
||||
|
||||
var rp http.Handler
|
||||
// when using anubis via Systemd and environment variables, then it is not possible to set targe to an empty string but only to space
|
||||
if strings.TrimSpace(*target) != "" {
|
||||
@@ -288,6 +267,8 @@ func main() {
|
||||
log.Fatalf("you can't set COOKIE_DOMAIN and COOKIE_DYNAMIC_DOMAIN at the same time")
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Thoth configuration
|
||||
switch {
|
||||
case *thothURL != "" && *thothToken == "":
|
||||
@@ -417,12 +398,21 @@ func main() {
|
||||
log.Fatalf("can't construct libanubis.Server: %v", err)
|
||||
}
|
||||
|
||||
wg := new(sync.WaitGroup)
|
||||
// install signal handler
|
||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
if *metricsBind != "" {
|
||||
wg.Add(1)
|
||||
go metricsServer(ctx, wg.Done)
|
||||
}
|
||||
|
||||
var h http.Handler
|
||||
h = s
|
||||
h = internal.RemoteXRealIP(*useRemoteAddress, *bindNetwork, h)
|
||||
h = internal.XForwardedForToXRealIP(h)
|
||||
h = internal.XForwardedForUpdate(*xffStripPrivate, h)
|
||||
h = internal.JA4H(h)
|
||||
|
||||
srv := http.Server{Handler: h, ErrorLog: internal.GetFilteredHTTPLogger()}
|
||||
listener, listenerUrl := setupListener(*bindNetwork, *bind)
|
||||
@@ -451,8 +441,6 @@ func main() {
|
||||
}
|
||||
}()
|
||||
|
||||
internal.SetHealth("anubis", healthv1.HealthCheckResponse_SERVING)
|
||||
|
||||
if err := srv.Serve(listener); !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -463,30 +451,20 @@ func metricsServer(ctx context.Context, done func()) {
|
||||
defer done()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/metrics", promhttp.Handler())
|
||||
mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
|
||||
st, ok := internal.GetHealth("anubis")
|
||||
if !ok {
|
||||
slog.Error("health service anubis does not exist, file a bug")
|
||||
}
|
||||
|
||||
switch st {
|
||||
case healthv1.HealthCheckResponse_NOT_SERVING:
|
||||
http.Error(w, "NOT OK", http.StatusInternalServerError)
|
||||
return
|
||||
case healthv1.HealthCheckResponse_SERVING:
|
||||
fmt.Fprintln(w, "OK")
|
||||
return
|
||||
default:
|
||||
http.Error(w, "UNKNOWN", http.StatusFailedDependency)
|
||||
return
|
||||
}
|
||||
})
|
||||
mux.Handle(anubis.BasePrefix+"/metrics", promhttp.Handler())
|
||||
|
||||
srv := http.Server{Handler: mux, ErrorLog: internal.GetFilteredHTTPLogger()}
|
||||
listener, metricsUrl := setupListener(*metricsBindNetwork, *metricsBind)
|
||||
slog.Debug("listening for metrics", "url", metricsUrl)
|
||||
|
||||
if *healthcheck {
|
||||
log.Println("running healthcheck")
|
||||
if err := doHealthCheck(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
c, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
|
||||
@@ -74,25 +74,6 @@ bots:
|
||||
weight:
|
||||
adjust: 10
|
||||
|
||||
# ## System load based checks.
|
||||
# # If the system is under high load, add weight.
|
||||
# - name: high-load-average
|
||||
# action: WEIGH
|
||||
# expression: load_1m >= 10.0 # make sure to end the load comparison in a .0
|
||||
# weight:
|
||||
# adjust: 20
|
||||
|
||||
## If your backend service is running on the same operating system as Anubis,
|
||||
## you can uncomment this rule to make the challenge easier when the system is
|
||||
## under low load.
|
||||
##
|
||||
## If it is not, remove weight.
|
||||
# - name: low-load-average
|
||||
# action: WEIGH
|
||||
# expression: load_15m <= 4.0 # make sure to end the load comparison in a .0
|
||||
# weight:
|
||||
# adjust: -10
|
||||
|
||||
# Generic catchall rule
|
||||
- name: generic-browser
|
||||
user_agent_regex: >-
|
||||
|
||||
@@ -1,223 +0,0 @@
|
||||
- name: uptime-robot
|
||||
user_agent_regex: UptimeRobot
|
||||
action: ALLOW
|
||||
# https://api.uptimerobot.com/meta/ips
|
||||
remote_addresses: [
|
||||
"3.12.251.153/32",
|
||||
"3.20.63.178/32",
|
||||
"3.77.67.4/32",
|
||||
"3.79.134.69/32",
|
||||
"3.105.133.239/32",
|
||||
"3.105.190.221/32",
|
||||
"3.133.226.214/32",
|
||||
"3.149.57.90/32",
|
||||
"3.212.128.62/32",
|
||||
"5.161.61.238/32",
|
||||
"5.161.73.160/32",
|
||||
"5.161.75.7/32",
|
||||
"5.161.113.195/32",
|
||||
"5.161.117.52/32",
|
||||
"5.161.177.47/32",
|
||||
"5.161.194.92/32",
|
||||
"5.161.215.244/32",
|
||||
"5.223.43.32/32",
|
||||
"5.223.53.147/32",
|
||||
"5.223.57.22/32",
|
||||
"18.116.205.62/32",
|
||||
"18.180.208.214/32",
|
||||
"18.192.166.72/32",
|
||||
"18.193.252.127/32",
|
||||
"24.144.78.39/32",
|
||||
"24.144.78.185/32",
|
||||
"34.198.201.66/32",
|
||||
"45.55.123.175/32",
|
||||
"45.55.127.146/32",
|
||||
"49.13.24.81/32",
|
||||
"49.13.130.29/32",
|
||||
"49.13.134.145/32",
|
||||
"49.13.164.148/32",
|
||||
"49.13.167.123/32",
|
||||
"52.15.147.27/32",
|
||||
"52.22.236.30/32",
|
||||
"52.28.162.93/32",
|
||||
"52.59.43.236/32",
|
||||
"52.87.72.16/32",
|
||||
"54.64.67.106/32",
|
||||
"54.79.28.129/32",
|
||||
"54.87.112.51/32",
|
||||
"54.167.223.174/32",
|
||||
"54.249.170.27/32",
|
||||
"63.178.84.147/32",
|
||||
"64.225.81.248/32",
|
||||
"64.225.82.147/32",
|
||||
"69.162.124.227/32",
|
||||
"69.162.124.235/32",
|
||||
"69.162.124.238/32",
|
||||
"78.46.190.63/32",
|
||||
"78.46.215.1/32",
|
||||
"78.47.98.55/32",
|
||||
"78.47.173.76/32",
|
||||
"88.99.80.227/32",
|
||||
"91.99.101.207/32",
|
||||
"128.140.41.193/32",
|
||||
"128.140.106.114/32",
|
||||
"129.212.132.140/32",
|
||||
"134.199.240.137/32",
|
||||
"138.197.53.117/32",
|
||||
"138.197.53.138/32",
|
||||
"138.197.54.143/32",
|
||||
"138.197.54.247/32",
|
||||
"138.197.63.92/32",
|
||||
"139.59.50.44/32",
|
||||
"142.132.180.39/32",
|
||||
"143.198.249.237/32",
|
||||
"143.198.250.89/32",
|
||||
"143.244.196.21/32",
|
||||
"143.244.196.211/32",
|
||||
"143.244.221.177/32",
|
||||
"144.126.251.21/32",
|
||||
"146.190.9.187/32",
|
||||
"152.42.149.135/32",
|
||||
"157.90.155.240/32",
|
||||
"157.90.156.63/32",
|
||||
"159.69.158.189/32",
|
||||
"159.223.243.219/32",
|
||||
"161.35.247.201/32",
|
||||
"167.99.18.52/32",
|
||||
"167.235.143.113/32",
|
||||
"168.119.53.160/32",
|
||||
"168.119.96.239/32",
|
||||
"168.119.123.75/32",
|
||||
"170.64.250.64/32",
|
||||
"170.64.250.132/32",
|
||||
"170.64.250.235/32",
|
||||
"178.156.181.172/32",
|
||||
"178.156.184.20/32",
|
||||
"178.156.185.127/32",
|
||||
"178.156.185.231/32",
|
||||
"178.156.187.238/32",
|
||||
"178.156.189.113/32",
|
||||
"178.156.189.249/32",
|
||||
"188.166.201.79/32",
|
||||
"206.189.241.133/32",
|
||||
"209.38.49.1/32",
|
||||
"209.38.49.206/32",
|
||||
"209.38.49.226/32",
|
||||
"209.38.51.43/32",
|
||||
"209.38.53.7/32",
|
||||
"209.38.124.252/32",
|
||||
"216.144.248.18/31",
|
||||
"216.144.248.21/32",
|
||||
"216.144.248.22/31",
|
||||
"216.144.248.24/30",
|
||||
"216.144.248.28/31",
|
||||
"216.144.248.30/32",
|
||||
"216.245.221.83/32",
|
||||
"2400:6180:10:200::56a0:b000/128",
|
||||
"2400:6180:10:200::56a0:c000/128",
|
||||
"2400:6180:10:200::56a0:e000/128",
|
||||
"2400:6180:100:d0::94b6:4001/128",
|
||||
"2400:6180:100:d0::94b6:5001/128",
|
||||
"2400:6180:100:d0::94b6:7001/128",
|
||||
"2406:da14:94d:8601:9d0d:7754:bedf:e4f5/128",
|
||||
"2406:da14:94d:8601:b325:ff58:2bba:7934/128",
|
||||
"2406:da14:94d:8601:db4b:c5ac:2cbe:9a79/128",
|
||||
"2406:da1c:9c8:dc02:7ae1:f2ea:ab91:2fde/128",
|
||||
"2406:da1c:9c8:dc02:7db9:f38b:7b9f:402e/128",
|
||||
"2406:da1c:9c8:dc02:82b2:f0fd:ee96:579/128",
|
||||
"2600:1f16:775:3a00:ac3:c5eb:7081:942e/128",
|
||||
"2600:1f16:775:3a00:37bf:6026:e54a:f03a/128",
|
||||
"2600:1f16:775:3a00:3f24:5bb0:95d7:5a6b/128",
|
||||
"2600:1f16:775:3a00:8c2c:2ba6:778f:5be5/128",
|
||||
"2600:1f16:775:3a00:91ac:3120:ff38:92b5/128",
|
||||
"2600:1f16:775:3a00:dbbe:36b0:3c45:da32/128",
|
||||
"2600:1f18:179:f900:71:af9a:ade7:d772/128",
|
||||
"2600:1f18:179:f900:2406:9399:4ae6:c5d3/128",
|
||||
"2600:1f18:179:f900:4696:7729:7bb3:f52f/128",
|
||||
"2600:1f18:179:f900:4b7d:d1cc:2d10:211/128",
|
||||
"2600:1f18:179:f900:5c68:91b6:5d75:5d7/128",
|
||||
"2600:1f18:179:f900:e8dd:eed1:a6c:183b/128",
|
||||
"2604:a880:800:14:0:1:68ba:d000/128",
|
||||
"2604:a880:800:14:0:1:68ba:e000/128",
|
||||
"2604:a880:800:14:0:1:68bb:0/128",
|
||||
"2604:a880:800:14:0:1:68bb:1000/128",
|
||||
"2604:a880:800:14:0:1:68bb:3000/128",
|
||||
"2604:a880:800:14:0:1:68bb:4000/128",
|
||||
"2604:a880:800:14:0:1:68bb:5000/128",
|
||||
"2604:a880:800:14:0:1:68bb:6000/128",
|
||||
"2604:a880:800:14:0:1:68bb:7000/128",
|
||||
"2604:a880:800:14:0:1:68bb:a000/128",
|
||||
"2604:a880:800:14:0:1:68bb:b000/128",
|
||||
"2604:a880:800:14:0:1:68bb:c000/128",
|
||||
"2604:a880:800:14:0:1:68bb:d000/128",
|
||||
"2604:a880:800:14:0:1:68bb:e000/128",
|
||||
"2604:a880:800:14:0:1:68bb:f000/128",
|
||||
"2607:ff68:107::4/128",
|
||||
"2607:ff68:107::14/128",
|
||||
"2607:ff68:107::33/128",
|
||||
"2607:ff68:107::48/127",
|
||||
"2607:ff68:107::50/125",
|
||||
"2607:ff68:107::58/127",
|
||||
"2607:ff68:107::60/128",
|
||||
"2a01:4f8:c0c:83fa::1/128",
|
||||
"2a01:4f8:c17:42e4::1/128",
|
||||
"2a01:4f8:c2c:9fc6::1/128",
|
||||
"2a01:4f8:c2c:beae::1/128",
|
||||
"2a01:4f8:1c1a:3d53::1/128",
|
||||
"2a01:4f8:1c1b:4ef4::1/128",
|
||||
"2a01:4f8:1c1b:5b5a::1/128",
|
||||
"2a01:4f8:1c1b:7ecc::1/128",
|
||||
"2a01:4f8:1c1c:11aa::1/128",
|
||||
"2a01:4f8:1c1c:5353::1/128",
|
||||
"2a01:4f8:1c1c:7240::1/128",
|
||||
"2a01:4f8:1c1c:a98a::1/128",
|
||||
"2a01:4f8:c012:c60e::1/128",
|
||||
"2a01:4f8:c013:c18::1/128",
|
||||
"2a01:4f8:c013:34c0::1/128",
|
||||
"2a01:4f8:c013:3b0f::1/128",
|
||||
"2a01:4f8:c013:3c52::1/128",
|
||||
"2a01:4f8:c013:3c53::1/128",
|
||||
"2a01:4f8:c013:3c54::1/128",
|
||||
"2a01:4f8:c013:3c55::1/128",
|
||||
"2a01:4f8:c013:3c56::1/128",
|
||||
"2a01:4ff:f0:bfd::1/128",
|
||||
"2a01:4ff:f0:2219::1/128",
|
||||
"2a01:4ff:f0:3e03::1/128",
|
||||
"2a01:4ff:f0:5f80::1/128",
|
||||
"2a01:4ff:f0:7fad::1/128",
|
||||
"2a01:4ff:f0:9c5f::1/128",
|
||||
"2a01:4ff:f0:b2f2::1/128",
|
||||
"2a01:4ff:f0:b6f1::1/128",
|
||||
"2a01:4ff:f0:d283::1/128",
|
||||
"2a01:4ff:f0:d3cd::1/128",
|
||||
"2a01:4ff:f0:e516::1/128",
|
||||
"2a01:4ff:f0:e9cf::1/128",
|
||||
"2a01:4ff:f0:eccb::1/128",
|
||||
"2a01:4ff:f0:efd1::1/128",
|
||||
"2a01:4ff:f0:fdc7::1/128",
|
||||
"2a01:4ff:2f0:193c::1/128",
|
||||
"2a01:4ff:2f0:27de::1/128",
|
||||
"2a01:4ff:2f0:3b3a::1/128",
|
||||
"2a03:b0c0:2:f0::bd91:f001/128",
|
||||
"2a03:b0c0:2:f0::bd92:1/128",
|
||||
"2a03:b0c0:2:f0::bd92:1001/128",
|
||||
"2a03:b0c0:2:f0::bd92:2001/128",
|
||||
"2a03:b0c0:2:f0::bd92:4001/128",
|
||||
"2a03:b0c0:2:f0::bd92:5001/128",
|
||||
"2a03:b0c0:2:f0::bd92:6001/128",
|
||||
"2a03:b0c0:2:f0::bd92:7001/128",
|
||||
"2a03:b0c0:2:f0::bd92:8001/128",
|
||||
"2a03:b0c0:2:f0::bd92:9001/128",
|
||||
"2a03:b0c0:2:f0::bd92:a001/128",
|
||||
"2a03:b0c0:2:f0::bd92:b001/128",
|
||||
"2a03:b0c0:2:f0::bd92:c001/128",
|
||||
"2a03:b0c0:2:f0::bd92:e001/128",
|
||||
"2a03:b0c0:2:f0::bd92:f001/128",
|
||||
"2a05:d014:1815:3400:6d:9235:c1c0:96ad/128",
|
||||
"2a05:d014:1815:3400:654f:bd37:724c:212b/128",
|
||||
"2a05:d014:1815:3400:90b4:4ef9:5631:b170/128",
|
||||
"2a05:d014:1815:3400:9779:d8e9:100a:9642/128",
|
||||
"2a05:d014:1815:3400:af29:e95e:64ff:df81/128",
|
||||
"2a05:d014:1815:3400:c7d6:f7f3:6cc1:30d1/128",
|
||||
"2a05:d014:1815:3400:d784:e5dd:8e0:67cb/128",
|
||||
]
|
||||
@@ -19,3 +19,5 @@ npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Kubernetes manifests
|
||||
/manifest
|
||||
@@ -5,7 +5,6 @@ COPY . .
|
||||
|
||||
RUN npm ci && npm run build
|
||||
|
||||
FROM ghcr.io/xe/nginx-micro
|
||||
COPY --from=build /app/build /www
|
||||
COPY ./manifest/cfg/nginx/nginx.conf /conf
|
||||
FROM docker.io/library/nginx:alpine
|
||||
COPY --from=build /app/build /usr/share/nginx/html
|
||||
LABEL org.opencontainers.image.source="https://github.com/TecharoHQ/anubis"
|
||||
@@ -20,9 +20,9 @@ If you rely on Anubis to keep your website safe, please consider sponsoring the
|
||||
|
||||
I am waiting to hear back from NLNet on if Anubis was selected for funding or not. Let's hope it is!
|
||||
|
||||
## Deprecation warning: `DIFFICULTY`
|
||||
## Deprecation warning: `DEFAULT_DIFFICULTY`
|
||||
|
||||
Anubis v1.20.0 is the last version to support the `DIFFICULTY` flag in the exact way it currently does. In future versions, this will be ineffectual and you should use the [custom threshold system](/docs/admin/configuration/thresholds) instead.
|
||||
Anubis v1.20.0 is the last version to support the `DEFAULT_DIFFICULTY` flag in the exact way it currently does. In future versions, this will be ineffectual and you should use the [custom threshold system](/docs/admin/configuration/thresholds) instead.
|
||||
|
||||
If this becomes an imposition in practice, this will be reverted.
|
||||
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
---
|
||||
slug: incident/TI-20250709-0001
|
||||
title: "TI-20250709-0001: IPv4 traffic failures for Techaro services"
|
||||
authors: [xe]
|
||||
tags: [incident]
|
||||
image: ./window-portal.jpg
|
||||
---
|
||||
|
||||

|
||||
|
||||
Techaro services were down for IPv4 traffic on July 9th, 2025. This blogpost is a report of what happened, what actions were taken to resolve the situation, and what actions are being done in the near future to prevent this problem. Enjoy this incident report!
|
||||
|
||||
{/* truncate */}
|
||||
|
||||
:::note
|
||||
|
||||
In other companies, this kind of documentation would be kept internal. At Techaro, we believe that you deserve radical candor and the truth. As such, we are proving our lofty words with actions by publishing details about how things go wrong publicly.
|
||||
|
||||
Everything past this point follows my standard incident root cause meeting template.
|
||||
|
||||
:::
|
||||
|
||||
This incident report will focus on the services affected, timeline of what happened at which stage of the incident, where we got lucky, the root cause analysis, and what action items are being planned or taken to prevent this from happening in the future.
|
||||
|
||||
## Timeline
|
||||
|
||||
All events take place on July 9th, 2025.
|
||||
|
||||
| Time (UTC) | Description |
|
||||
| :--------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 12:32 | Uptime Kuma reports that another unrelated website on the same cluster was timing out. |
|
||||
| 12:33 | Uptime Kuma reports that Thoth's production endpoint is failing gRPC health checks. |
|
||||
| 12:35 | Investigation begins, [announcement made on Xe's Bluesky](https://bsky.app/profile/xeiaso.net/post/3ltjtdczpwc2x) due to the impact including their personal blog. |
|
||||
| 12:39 | `nginx-ingress` logs on the production cluster show IPv6 traffic but an abrupt cutoff in IPv4 traffic around 12:32 UTC. Ticket is opened with the hosting provider. |
|
||||
| 12:41 | IPv4 traffic resumes long enough for Uptime Kuma to report uptime, but then immediately fails again. |
|
||||
| 12:46 | IPv4 traffic resumes long enough for Uptime Kuma to report uptime, but then immediately fails again. (repeat instances of this have been scrubbed, but it happened about every 5-10 minutes) |
|
||||
| 12:48 | First reply from the hosting provider. |
|
||||
| 12:57 | Reply to hosting provider, ask to reboot the load balancer. |
|
||||
| 13:00 | Incident responder because busy due to a meeting under the belief that the downtime was out of their control and that uptime monitoring software would let them know if it came back up. |
|
||||
| 13:20 | Incident responder ended meeting and went back to monitoring downtime and preparing this document. |
|
||||
| 13:34 | IPv4 traffic starts to show up in the `ingress-nginx` logs. |
|
||||
| 13:35 | All services start to report healthy. Incident status changes to monitoring. |
|
||||
| 13:48 | Incident closed. |
|
||||
| 14:07 | Incident re-opened. Issues seem to be manifesting as BGP issues in the upstream provider. |
|
||||
| 14:10 | IPv4 traffic resumes and then stops. |
|
||||
| 14:18 | IPv4 traffic resumes again. Incident status changes to monitoring. |
|
||||
| 14:40 | Incident closed. |
|
||||
|
||||
## Services affected
|
||||
|
||||
| Service name | User impact |
|
||||
| :-------------------------------------------------- | :----------------- |
|
||||
| [Anubis Docs](https://anubis.techaro.lol) (IPv4) | Connection timeout |
|
||||
| [Anubis Docs](https://anubis.techaro.lol) (IPv6) | None |
|
||||
| [Thoth](/docs/admin/thoth/) (IPv4) | Connection timeout |
|
||||
| [Thoth](/docs/admin/thoth/) (IPv6) | None |
|
||||
| Other websites colocated on the same cluster (IPv4) | Connection timeout |
|
||||
| Other websites colocated on the same cluster (IPv6) | None |
|
||||
|
||||
## Root cause analysis
|
||||
|
||||
In simplify server management, Techaro runs a [Kubernetes](https://kubernetes.io/) cluster on [Vultr VKE](https://www.vultr.com/kubernetes/) (Vultr Kubernetes Engine). When you do this, Vultr needs to provision a [load balancer](https://docs.vultr.com/how-to-use-a-vultr-load-balancer-with-vke) to bridge the gap between the outside world and the Kubernetes world, kinda like this:
|
||||
|
||||
```mermaid
|
||||
---
|
||||
title: Overall architecture
|
||||
---
|
||||
|
||||
flowchart LR
|
||||
UT(User Traffic)
|
||||
subgraph Provider Infrastructure
|
||||
LB[Load Balancer]
|
||||
end
|
||||
subgraph Kubernetes
|
||||
IN(ingress-nginx)
|
||||
TH(Thoth)
|
||||
AN(Anubis Docs)
|
||||
OS(Other sites)
|
||||
|
||||
IN --> TH
|
||||
IN --> AN
|
||||
IN --> OS
|
||||
end
|
||||
|
||||
UT --> LB --> IN
|
||||
```
|
||||
|
||||
Techaro controls everything inside the Kubernetes side of that diagram. Anything else is out of our control. That load balancer is routed to the public internet via [Border Gateway Protocol (BGP)](https://en.wikipedia.org/wiki/Border_Gateway_Protocol).
|
||||
|
||||
If there is an interruption with the BGP sessions in the upstream provider, this can manifest as things either not working or inconsistently working. This is made more difficult by the fact that the IPv4 and IPv6 internets are technically separate networks. With this in mind, it's very possible to have IPv4 traffic fail but not IPv6 traffic.
|
||||
|
||||
The root cause is that the hosting provider we use for production services had flapping IPv4 BGP sessions in its Toronto region. When this happens all we can do is open a ticket and wait for it to come back up.
|
||||
|
||||
## Where we got lucky
|
||||
|
||||
The Uptime Kuma instance that caught this incident runs on an IPv4-only network. If it was dual stack, this would not have been caught as quickly.
|
||||
|
||||
The `ingress-nginx` logs print IP addresses of remote clients to the log feed. If this was not the case, it would be much more difficult to find this error.
|
||||
|
||||
## Action items
|
||||
|
||||
- A single instance of downtime like this is not enough reason to move providers. Moving providers because of this is thus out of scope.
|
||||
- Techaro needs a status page hosted on a different cloud provider than is used for the production cluster (`TecharoHQ/TODO#6`).
|
||||
- Health checks for IPv4 and IPv6 traffic need to be created (`TecharoHQ/TODO#7`).
|
||||
- Remove the requirement for [Anubis to pass Thoth health checks before it can start if Thoth is enabled](https://github.com/TecharoHQ/anubis/pull/794).
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 30 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 62 KiB |
@@ -1,369 +0,0 @@
|
||||
---
|
||||
slug: release/v1.21.1
|
||||
title: Anubis v1.21.1 is now available!
|
||||
authors: [xe]
|
||||
tags: [release]
|
||||
image: anubis-i18n.webp
|
||||
---
|
||||
|
||||

|
||||
|
||||
Hey all!
|
||||
|
||||
Recently we released [Anubis v1.21.1: Minfilia Warde (Echo 1)](https://github.com/TecharoHQ/anubis/releases/tag/v1.21.1). This is a fairly meaty release and like [last time](../2025-06-27-release-1.20.0/index.mdx) this blogpost will tell you what you need to know before you update. Kick back, get some popcorn and let's dig into this!
|
||||
|
||||
{/* truncate */}
|
||||
|
||||
In this release, Anubis becomes internationalized, gains the ability to use system load as input to issuing challenges, finally fixes the "invalid response" after "success" bug, and more! Please read these notes before upgrading as the changes are big enough that administrators should take action to ensure that the upgrade goes smoothly.
|
||||
|
||||
This release is brought to you by [FreeCAD](https://www.freecad.org/), an open-source computer aided design tool that lets you design things for the real world.
|
||||
|
||||
## What's in this release?
|
||||
|
||||
The biggest change is that the ["invalid response" after "success" bug](https://github.com/TecharoHQ/anubis/issues/564) is now finally fixed for good by totally rewriting how [Anubis' challenge issuance flow works](#challenge-flow-v2).
|
||||
|
||||
This release gives Anubis the following features:
|
||||
|
||||
- [Internationalization support](#internationalization), allowing Anubis to render its messages in the human language you speak.
|
||||
- Anubis now supports the [`missingHeader`](#missingHeader-function) function to assert the absence of headers in requests.
|
||||
- Anubis now has the ability to [store data persistently on the server](#persistent-data-storage).
|
||||
- Anubis can use [the system load average](#load-average-checks) as a factor to determine if it needs to filter traffic or not.
|
||||
- Add `COOKIE_SECURE` option to set the cookie [Secure flag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Cookies#block_access_to_your_cookies)
|
||||
- Sets cookie defaults to use [SameSite: None](https://web.dev/articles/samesite-cookies-explained)
|
||||
- Allow [Common Crawl](https://commoncrawl.org/) by default so scrapers have less incentive to scrape
|
||||
- Add `/healthz` metrics route for use in platform-based health checks.
|
||||
- Start exposing JA4H fingerprints for later use in CEL expressions.
|
||||
|
||||
And this release also fixes the following bugs:
|
||||
|
||||
- [Challenge issuance has been totally rewritten](#challenge-flow-v2) to finally squash the infamous ["invalid response" after "success" bug](https://github.com/TecharoHQ/anubis/issues/564) for good.
|
||||
- In order to reduce confusion, the "Success" interstitial that shows up when you pass a proof of work challenge has been removed.
|
||||
- Don't block Anubis starting up if [Thoth](/docs/admin/thoth/) health checks fail.
|
||||
- The "Try again" button on the error page has been fixed. Previously it meant "try the solution again" instead of "try the challenge again".
|
||||
- In certain cases, a user could be stuck with a test cookie that is invalid, locking them out of the service for up to half an hour. This has been fixed with better validation of this case and clearing the cookie.
|
||||
- "Proof of work" has been removed from the branding due to some users having extremely negative connotations with it.
|
||||
|
||||
We try to avoid introducing breaking changes as much as possible, but these are the changes that may be relevant for you as an administrator:
|
||||
|
||||
- The [challenge format](#challenge-format-change) has been changed in order to account for [the new challenge issuance flow](#challenge-flow-v2).
|
||||
- The [systemd service `RuntimeDirectory` has been changed](#breaking-change-systemd-runtimedirectory-change).
|
||||
|
||||
### Sponsoring the project
|
||||
|
||||
If you rely on Anubis to keep your website safe, please consider sponsoring the project on [GitHub Sponsors](https://github.com/sponsors/Xe) or [Patreon](https://patreon.com/cadey). Funding helps pay hosting bills and offset the time spent on making this project the best it can be. Every little bit helps and when enough money is raised, [I can make Anubis my full-time job](https://github.com/TecharoHQ/anubis/discussions/278).
|
||||
|
||||
Once this pie chart is at 100%, I can start to reduce my hours at my day job as most of my needs will be met (pre-tax):
|
||||
|
||||
```mermaid
|
||||
pie title Funding update
|
||||
"GitHub Sponsors" : 29
|
||||
"Patreon" : 14
|
||||
"Remaining" : 56
|
||||
```
|
||||
|
||||
I am waiting to hear back from NLNet on if Anubis was selected for funding or not. Let's hope it is!
|
||||
|
||||
## New features
|
||||
|
||||
### Internationalization
|
||||
|
||||
Anubis now supports localized responses. Locales can be added in [lib/localization/locales/](https://github.com/TecharoHQ/anubis/tree/main/lib/localization/locales). This release includes support for the following languages:
|
||||
|
||||
- [Brazilian Portugese](https://github.com/TecharoHQ/anubis/pull/726)
|
||||
- [Chinese (Simplified)](https://github.com/TecharoHQ/anubis/pull/774)
|
||||
- [Chinese (Traditional)](https://github.com/TecharoHQ/anubis/pull/759)
|
||||
- [Czech](https://github.com/TecharoHQ/anubis/pull/849)
|
||||
- English
|
||||
- [Estonian](https://github.com/TecharoHQ/anubis/pull/783)
|
||||
- [Filipino](https://github.com/TecharoHQ/anubis/pull/775)
|
||||
- [Finnish](https://github.com/TecharoHQ/anubis/pull/863)
|
||||
- [French](https://github.com/TecharoHQ/anubis/pull/716)
|
||||
- [German](https://github.com/TecharoHQ/anubis/pull/741)
|
||||
- [Japanese](https://github.com/TecharoHQ/anubis/pull/772)
|
||||
- [Icelandic](https://github.com/TecharoHQ/anubis/pull/780)
|
||||
- [Italian](https://github.com/TecharoHQ/anubis/pull/778)
|
||||
- [Norwegian](https://github.com/TecharoHQ/anubis/pull/855)
|
||||
- [Russian](https://github.com/TecharoHQ/anubis/pull/882)
|
||||
- [Spanish](https://github.com/TecharoHQ/anubis/pull/716)
|
||||
- [Turkish](https://github.com/TecharoHQ/anubis/pull/751)
|
||||
|
||||
If facts or local regulations demand, you can set Anubis default language with the `FORCED_LANGUAGE` environment variable or the `--forced-language` command line argument:
|
||||
|
||||
```sh
|
||||
FORCED_LANGUAGE=de
|
||||
```
|
||||
|
||||
## Big ticket bug fixes
|
||||
|
||||
These issues affect every user of Anubis. Administrators should upgrade Anubis as soon as possible to mitigate them.
|
||||
|
||||
### Fix event loop thrashing when solving a proof of work challenge
|
||||
|
||||
Anubis has a progress bar so that users can have something moving while it works. This gives users more confidence that something is happening and that the website is not being malicious with CPU usage. However, the way it was implemented way back in [#87](https://github.com/TecharoHQ/anubis/pull/87) had a subtle bug:
|
||||
|
||||
```js
|
||||
if (
|
||||
(nonce > oldNonce) | 1023 && // we've wrapped past 1024
|
||||
(nonce >> 10) % threads === threadId // and it's our turn
|
||||
) {
|
||||
postMessage(nonce);
|
||||
}
|
||||
```
|
||||
|
||||
The logic here looks fine but is subtly wrong as was reported in [#877](https://github.com/TecharoHQ/anubis/issues/877) by the main Pale Moon developer.
|
||||
|
||||
For context, `nonce` is a counter that increments by the worker count every loop. This is intended to spread the load between CPU cores as such:
|
||||
|
||||
| Iteration | Worker ID | Nonce |
|
||||
| :-------- | :-------- | :---- |
|
||||
| 1 | 0 | 0 |
|
||||
| 1 | 1 | 1 |
|
||||
| 2 | 0 | 2 |
|
||||
| 2 | 1 | 3 |
|
||||
|
||||
And so on. This makes the proof of work challenge as fast as it can possibly be so that Anubis quickly goes away and you can enjoy the service it is protecting.
|
||||
|
||||
The incorrect part of this is the boolean logic, specifically the part with the bitwise or `|`. I think the intent was to use a logical or (`||`), but this had the effect of making the `postMessage` handler fire on every iteration. The intent of this snippet (as the comment clearly indicates) is to make sure that the main event loop is only updated with the worker status every 1024 iterations per worker. This had the opposite effect, causing a lot of messages to be sent from workers to the parent JavaScript context.
|
||||
|
||||
This is bad for the event loop.
|
||||
|
||||
Instead, I have ripped out that statement and replaced it with a much simpler increment only counter that fires every 1024 iterations. Additionally, only the first thread communicates back to the parent process. This does mean that in theory the other workers could be ahead of the first thread (posting a message out of a worker has a nonzero cost), but in practice I don't think this will be as much of an issue as the current behaviour is.
|
||||
|
||||
The root cause of the stack exhaustion is likely the pressure caused by all of the postMessage futures piling up. Maybe the larger stack size in 64 bit environments is causing this to be fine there, maybe it's some combination of newer hardware in 64 bit systems making this not be as much of a problem due to it being able to handle events fast enough to keep up with the pressure.
|
||||
|
||||
Either way, thanks much to [@wolfbeast](https://github.com/wolfbeast) and the Pale Moon community for finding this. This will make Anubis faster for everyone!
|
||||
|
||||
### Fix potential memory leak when discovering a solution
|
||||
|
||||
In some cases, the parallel solution finder in Anubis could cause all of the worker promises to leak due to the fact the promises were being improperly terminated. A recursion bomb happens in the following scenario:
|
||||
|
||||
1. A worker sends a message indicating it found a solution to the proof of work challenge.
|
||||
2. The `onmessage` handler for that worker calls `terminate()`
|
||||
3. Inside `terminate()`, the parent process loops through all other workers and calls `w.terminate()` on them.
|
||||
4. It's possible that terminating a worker could lead to the `onerror` event handler.
|
||||
5. This would create a recursive loop of `onmessage` -> `terminate` -> `onerror` -> `terminate` -> `onerror` and so on.
|
||||
|
||||
This infinite recursion quickly consumes all available stack space, but this has never been noticed in development because all of my computers have at least 64Gi of ram provisioned to them under the axiom paying for more ram is cheaper than paying in my time spent having to work around not having enough ram. Additionally, ia32 has a smaller base stack size, which means that they will run into this issue much sooner than users on other CPU architectures will.
|
||||
|
||||
The fix adds a boolean `settled` flag to prevent termination from running more than once.
|
||||
|
||||
## Expressions features
|
||||
|
||||
Anubis v1.21.1 adds additional [expressions](/docs/admin/configuration/expressions) features so that you can make your request matching even more granular.
|
||||
|
||||
### `missingHeader` function
|
||||
|
||||
Anubis [expressions](/docs/admin/configuration/expressions) have [a few functions exposed](/docs/admin/configuration/expressions/#functions-exposed-to-anubis-expressions). Anubis v1.21.1 adds the `missingHeader` function, allowing you to assert the _absence_ of a header in requests.
|
||||
|
||||
Let's say you're getting a lot of requests from clients that are pretending to be Google Chrome. Google Chrome sends a few signals to web servers, the main one of them is the [`Sec-Ch-Ua`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-CH-UA). Sec-CH-UA is part of Google's [User Agent Client Hints](https://wicg.github.io/ua-client-hints/#sec-ch-ua) proposal, but it being present is a sign that the client is more likely Google Chrome than not. With the `missingHeader` function, you can write a rule to [add weight](/docs/admin/policies/#request-weight) to requests without `Sec-Ch-Ua` that claim to be Google Chrome.
|
||||
|
||||
```yaml
|
||||
# Adds weight clients that claim to be Google Chrome without setting Sec-Ch-Ua
|
||||
- name: old-chrome
|
||||
action: WEIGH
|
||||
weight:
|
||||
adjust: 10
|
||||
expression:
|
||||
all:
|
||||
- userAgent.matches("Chrome/[1-9][0-9]?\\.0\\.0\\.0")
|
||||
- missingHeader(headers, "Sec-Ch-Ua")
|
||||
```
|
||||
|
||||
When combined with [weight thresholds](/docs/admin/configuration/thresholds), this allows you to make requests that don't match the signature of Google Chrome more suspicious, which will make them have a more difficult challenge.
|
||||
|
||||
### Load average checks
|
||||
|
||||
Anubis can dynamically take action [based on the system load average](/docs/admin/configuration/expressions/#using-the-system-load-average), allowing you to write rules like this:
|
||||
|
||||
```yaml
|
||||
## System load based checks.
|
||||
# If the system is under high load for the last minute, add weight.
|
||||
- name: high-load-average
|
||||
action: WEIGH
|
||||
expression: load_1m >= 10.0 # make sure to end the load comparison in a .0
|
||||
weight:
|
||||
adjust: 20
|
||||
|
||||
# If it is not for the last 15 minutes, remove weight.
|
||||
- name: low-load-average
|
||||
action: WEIGH
|
||||
expression: load_15m <= 4.0 # make sure to end the load comparison in a .0
|
||||
weight:
|
||||
adjust: -10
|
||||
```
|
||||
|
||||
Something to keep in mind about system load average is that it is not aware of the number of cores the system has. If you have a 16 core system that has 16 processes running but none of them is hogging the CPU, then you will get a load average below 16. If you are in doubt, make your "high load" metric at least two times the number of CPU cores and your "low load" metric at least half of the number of CPU cores. For example:
|
||||
|
||||
| Kind | Core count | Load threshold |
|
||||
| --------: | :--------- | :------------- |
|
||||
| high load | 4 | `8.0` |
|
||||
| low load | 4 | `2.0` |
|
||||
| high load | 16 | `32.0` |
|
||||
| low load | 16 | `8` |
|
||||
|
||||
Also keep in mind that this does not account for other kinds of latency like I/O latency or downstream API response latency. A system can have its web applications unresponsive due to high latency from a MySQL server but still have that web application server report a load near or at zero.
|
||||
|
||||
:::note
|
||||
|
||||
This does not work if you are using Kubernetes.
|
||||
|
||||
:::
|
||||
|
||||
When combined with [weight thresholds](/docs/admin/configuration/thresholds), this allows you to make incoming sessions "back off" while the server is under high load.
|
||||
|
||||
## Challenge flow v2
|
||||
|
||||
The main goal of Anubis is to weigh the risks of incoming requests in order to protect upstream resources against abusive clients like badly written scrapers. In order to separate "good" clients (like users wanting to learn from a website's content) from "bad" clients, Anubis issues [challenges](/docs/category/challenges).
|
||||
|
||||
Previously the Anubis challenge flow looked like this:
|
||||
|
||||
```mermaid
|
||||
---
|
||||
title: Old Anubis challenge flow
|
||||
---
|
||||
flowchart LR
|
||||
user(User Browser)
|
||||
subgraph Anubis
|
||||
mIC{Challenge?}
|
||||
ic(Issue Challenge)
|
||||
rp(Proxy to service)
|
||||
mIC -->|User needs a challenge| ic
|
||||
mIC -->|User does not need a challenge| rp
|
||||
end
|
||||
target(Target Service)
|
||||
rp --> target
|
||||
user --> mIC
|
||||
ic -->|Pass a challenge| user
|
||||
target -->|Site data| users
|
||||
```
|
||||
|
||||
In order to issue a challenge, Anubis generated a challenge string based on request metadata that we assumed wouldn't drastically change between requests, including but not limited to:
|
||||
|
||||
- The client's User-Agent string.
|
||||
- The client [`Accept-Language` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept-Language) value.
|
||||
- The client's IP address.
|
||||
|
||||
Anubis also didn't store any information about challenges so that it can remain lightweight and handle the onslaught of requests from scrapers. The assumption was that the challenge string function was idempotent per client across time. What actually ended up happening was something like this:
|
||||
|
||||
```mermaid
|
||||
---
|
||||
title: Anubis challenge string idempotency
|
||||
---
|
||||
sequenceDiagram
|
||||
User->>+Anubis: GET /wiki/some-page
|
||||
Anubis->>+Make Challenge: Generate a challenge string
|
||||
Make Challenge->>-Anubis: Challenge string: taco salad
|
||||
Anubis->>-User: HTTP 401 solve a challenge
|
||||
User->>+Anubis: GET internal-api/pass-challenge
|
||||
Anubis->>+Make Challenge: Generate a challenge string
|
||||
Make Challenge->>-Anubis: Challenge string: burrito bar
|
||||
Anubis->>+User: Error: invalid response
|
||||
```
|
||||
|
||||
Various attempts were made to fix this. All of these ended up failing. Many difficulties were discovered including but not limited to:
|
||||
|
||||
- Removing `Accept-Language` from consideration because [Chrome randomizes the contents of `Accept-Language` to reduce fingerprinting](https://github.com/explainers-by-googlers/reduce-accept-language), a behaviour which [causes a lot of confusion](https://www.reddit.com/r/chrome/comments/nhpnez/google_chrome_is_randomly_switching_languages_on/) for users with multiple system languages selected.
|
||||
- [IPv6 privacy extensions](https://www.internetsociety.org/resources/deploy360/2014/privacy-extensions-for-ipv6-slaac/) mean that each request could be coming from a different IP address (at least one legitimate user in the wild has been observed to have a different IP address per TCP session across an entire `/48`).
|
||||
- Some [US mobile phone carriers make it too easy for your IP address to drastically change](https://news.ycombinator.com/item?id=32038215) without user input.
|
||||
- [Happy eyeballs](https://en.wikipedia.org/wiki/Happy_Eyeballs) means that some requests can come in over IPv4 and some requests can come in over IPv6.
|
||||
- To make things worse, you can't even assert that users are from the same [BGP autonomous system](<https://en.wikipedia.org/wiki/Autonomous_system_(Internet)>) because some users could have ISPs that are IPv4 only, forcing them to use a different IP address space to get IPv6 internet access. This sounds like it's rare enough, but I personally have to do this even though I pay for 8 gigabit fiber from my ISP and only get IPv4 service from them.
|
||||
|
||||
Amusingly enough, the only part of this that has survived is the assertion that a user hasn't changed their `User-Agent` string. Maybe [that one guy that sets his Chrome version to `150`](https://github.com/TecharoHQ/anubis/issues/239) would have issues, but so far I've not seen any evidence that a client randomly changing their user agent between challenge issuance and solving can possibly be legitimate.
|
||||
|
||||
As a result, the entire subsystem that generated challenges before had to be ripped out and rewritten from scratch.
|
||||
|
||||
It was replaced with a new flow that stores data on the server side, compares that data against what the client responds with, and then checks pass/fail that way:
|
||||
|
||||
```mermaid
|
||||
---
|
||||
title: New challenge flow
|
||||
---
|
||||
sequenceDiagram
|
||||
User->>+Anubis: GET /wiki/some-page
|
||||
Anubis->>+Make Challenge: Generate a challenge string
|
||||
Make Challenge->>+Store: Store info for challenge 1234
|
||||
Make Challenge->>-Anubis: Challenge string: taco salad, ID 1234
|
||||
Anubis->>-User: HTTP 401 solve a challenge
|
||||
User->>+Anubis: GET internal-api/pass-challenge, challenge 1234
|
||||
Anubis->>+Validate Challenge: verify challenge 1234
|
||||
Validate Challenge->>+Store: Get info for challenge 1234
|
||||
Store->>-Validate Challenge: Here you go!
|
||||
Validate Challenge->>-Anubis: Valid ✅
|
||||
Anubis->>+User: Here's a cookie to get past Anubis
|
||||
```
|
||||
|
||||
As a result, the [challenge format](#challenge-format-change) had to change. Old cookies will still be validated, but the next minor version (v1.22.0) will include validation to ensure that all challenges are accounted for on the server side. This data is stored in the active [storage backend](/docs/admin/policies/#storage-backends) for up to 30 minutes. This also fixes [#746](https://github.com/TecharoHQ/anubis/issues/746) and other similar instances of this issue.
|
||||
|
||||
### Challenge format change
|
||||
|
||||
Previously Anubis did no accounting for challenges that it issued. This means that if Anubis restarted during a client, the client would be able to proceed once Anubis came back online.
|
||||
|
||||
During the upgrade to v1.21.0 and when v1.21.0 (or later) restarts with the [in-memory storage backend](/docs/admin/policies/#memory), you may see a higher rate of failed challenges than normal. If this persists beyond a few minutes, [open an issue](https://github.com/TecharoHQ/anubis/issues/new).
|
||||
|
||||
If you are using the in-memory storage backend, please consider using [a different storage backend](/docs/admin/policies/#storage-backends).
|
||||
|
||||
### Storage
|
||||
|
||||
Anubis offers a few different storage backends depending on your needs:
|
||||
|
||||
| Backend | Description |
|
||||
| :--------------------------------------- | :------------------------------------------------------------------------------------------------------------- |
|
||||
| [`memory`](/docs/admin/policies/#memory) | An in-memory hashmap that is cleared when Anubis is restarted. |
|
||||
| [`bbolt`](/docs/admin/policies/#bbolt) | A memory-mapped key/value store that can persist between Anubis restarts. |
|
||||
| [`valkey`](/docs/admin/policies/#valkey) | A networked key/value store that can persist between Anubis restarts and coordinate across multiple instances. |
|
||||
|
||||
Please review the documentation for each storage method to figure out the one best for your needs. If you aren't sure, consult this diagram:
|
||||
|
||||
```mermaid
|
||||
---
|
||||
title: What storage backend do I need?
|
||||
---
|
||||
flowchart TD
|
||||
OneInstance{Do you only have
|
||||
one instance of
|
||||
Anubis?}
|
||||
Persistence{Do you have
|
||||
persistent disk
|
||||
access in your
|
||||
environment?}
|
||||
bbolt[(bbolt)]
|
||||
memory[(memory)]
|
||||
valkey[(valkey)]
|
||||
OneInstance -->|Yes| Persistence
|
||||
OneInstance -->|No| valkey
|
||||
Persistence -->|Yes| bbolt
|
||||
Persistence -->|No| memory
|
||||
```
|
||||
|
||||
## Breaking change: systemd `RuntimeDirectory` change
|
||||
|
||||
The following potentially breaking change applies to native installs with systemd only:
|
||||
|
||||
Each instance of systemd service template now has a unique `RuntimeDirectory`, as opposed to each instance of the service sharing a `RuntimeDirectory`. This change was made to avoid [the `RuntimeDirectory` getting nuked](https://github.com/TecharoHQ/anubis/issues/748) any time one of the Anubis instances restarts.
|
||||
|
||||
If you configured Anubis' unix sockets to listen on `/run/anubis/foo.sock` for instance `anubis@foo`, you will need to configure Anubis to listen on `/run/anubis/foo/foo.sock` and additionally configure your HTTP load balancer as appropriate.
|
||||
|
||||
If you need the legacy behaviour, install this [systemd unit dropin](https://www.flatcar.org/docs/latest/setup/systemd/drop-in-units/):
|
||||
|
||||
```systemd
|
||||
# /etc/systemd/system/anubis@.service.d/50-runtimedir.conf
|
||||
[Service]
|
||||
RuntimeDirectory=anubis
|
||||
```
|
||||
|
||||
Just keep in mind that this will cause problems when Anubis restarts.
|
||||
|
||||
## What's up next?
|
||||
|
||||
The biggest things we want to do in the next release (in no particular order):
|
||||
|
||||
- A rewrite of bot checking rule configuration syntax to make it less ambiguous.
|
||||
- [JA4](https://blog.foxio.io/ja4+-network-fingerprinting) (and other forms of) fingerprinting and coordination with [Thoth](/docs/admin/thoth/) to allow clients with high aggregate pass rates through without seeing Anubis at all.
|
||||
- Advanced heuristics for [users of the unbranded variant of Anubis](/docs/admin/botstopper/).
|
||||
- Optimize the release flow so that releases can be triggered and executed by continuous integration tools. The ultimate goal is to make it possible to release Anubis in 15 minutes after pressing a single "mint release" button.
|
||||
- Add "hot reloading" support to Anubis, allowing administrators to update the rules without restarting the service.
|
||||
- Fix [multiple slash support](https://github.com/TecharoHQ/anubis/issues/754) for web applications that require optional path variables.
|
||||
- Add weight to "brand new" clients.
|
||||
- Implement a "maze" feature that tries to get crawlers ensnared in a maze of random links so that clients that are more than 20 links in can be reported to the home base.
|
||||
- Open [Thoth-based advanced checks](/docs/admin/thoth/) to more users with an easier onboarding flow.
|
||||
- More smoke tests including for browsers like [Pale Moon](https://www.palemoon.org/).
|
||||
@@ -13,177 +13,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
<!-- This changes the project to: -->
|
||||
|
||||
## v1.21.3: Minfilia Warde - Echo 3
|
||||
|
||||
### Fixes
|
||||
|
||||
#### Fixes a problem with nonstandard URLs and redirects
|
||||
|
||||
Fixes [GHSA-jhjj-2g64-px7c](https://github.com/TecharoHQ/anubis/security/advisories/GHSA-jhjj-2g64-px7c).
|
||||
|
||||
This could allow an attacker to craft an Anubis pass-challenge URL that forces a redirect to nonstandard URLs, such as the `javascript:` scheme which executes arbitrary JavaScript code in a browser context when the user clicks the "Try again" button.
|
||||
|
||||
This has been fixed by disallowing any URLs without the scheme `http` or `https`.
|
||||
|
||||
Additionally, the "Try again" button has been fixed to completely ignore the user-supplied redirect location. It now redirects to the home page (`/`).
|
||||
|
||||
## v1.21.2: Minfilia Warde - Echo 2
|
||||
|
||||
This contained an incomplete fix for [GHSA-jhjj-2g64-px7c](https://github.com/TecharoHQ/anubis/security/advisories/GHSA-jhjj-2g64-px7c). Do not use this version.
|
||||
|
||||
## v1.21.1: Minfilia Warde - Echo 1
|
||||
|
||||
- Expired records are now properly removed from bbolt databases ([#848](https://github.com/TecharoHQ/anubis/pull/848)).
|
||||
- Fix hanging on service restart ([#853](https://github.com/TecharoHQ/anubis/issues/853))
|
||||
|
||||
### Added
|
||||
|
||||
Anubis now supports the [`missingHeader`](./admin/configuration/expressions.mdx#missingHeader) to assert the absence of headers in requests.
|
||||
|
||||
#### New locales
|
||||
|
||||
Anubis now supports these new languages:
|
||||
|
||||
- [Czech](https://github.com/TecharoHQ/anubis/pull/849)
|
||||
- [Finnish](https://github.com/TecharoHQ/anubis/pull/863)
|
||||
- [Norwegian Bokmål](https://github.com/TecharoHQ/anubis/pull/855)
|
||||
- [Norwegian Nynorsk](https://github.com/TecharoHQ/anubis/pull/855)
|
||||
- [Russian](https://github.com/TecharoHQ/anubis/pull/882)
|
||||
|
||||
### Fixes
|
||||
|
||||
#### Fix ["error: can't get challenge"](https://github.com/TecharoHQ/anubis/issues/869) when details about a challenge can't be found in the server side state
|
||||
|
||||
v1.21.0 changed the core challenge flow to maintain information about challenges on the server side instead of only doing them via stateless idempotent generation functions and relying on details to not change. There was a subtle bug introduced in this change: if a client has an unknown challenge ID set in its test cookie, Anubis will clear that cookie and then throw an HTTP 500 error.
|
||||
|
||||
This has been fixed by making Anubis throw a new challenge page instead.
|
||||
|
||||
#### Fix event loop thrashing when solving a proof of work challenge
|
||||
|
||||
Previously the "fast" proof of work solver had a fragment of JavaScript that attempted to only post an update about proof of work progress to the main browser window every 1024 iterations. This fragment of JavaScript was subtly incorrect in a way that passed review but actually made the workers send an update back to the main thread every iteration. This caused a pileup of unhandled async calls (similar to a socket accept() backlog pileup in Unix) that caused stack space exhaustion.
|
||||
|
||||
This has been fixed in the following ways:
|
||||
|
||||
1. The complicated boolean logic has been totally removed in favour of a worker-local iteration counter.
|
||||
2. The progress bar is updated by worker `0` instead of all workers.
|
||||
|
||||
Hopefully this should limit the event loop thrashing and let ia32 browsers (as well as any environment with a smaller stack size than amd64 and aarch64 seem to have) function normally when processing Anubis proof of work challenges.
|
||||
|
||||
#### Fix potential memory leak when discovering a solution
|
||||
|
||||
In some cases, the parallel solution finder in Anubis could cause all of the worker promises to leak due to the fact the promises were being improperly terminated. This was fixed by having Anubis debounce worker termination instead of allowing it to potentially recurse infinitely.
|
||||
|
||||
## v1.21.0: Minfilia Warde
|
||||
|
||||
> Please, be at ease. You are among friends here.
|
||||
|
||||
In this release, Anubis becomes internationalized, gains the ability to use system load as input to issuing challenges, finally fixes the "invalid response" after "success" bug, and more! Please read these notes before upgrading as the changes are big enough that administrators should take action to ensure that the upgrade goes smoothly.
|
||||
|
||||
### Big ticket changes
|
||||
|
||||
The biggest change is that the ["invalid response" after "success" bug](https://github.com/TecharoHQ/anubis/issues/564) is now finally fixed for good by totally rewriting how Anubis' challenge issuance flow works. Instead of generating challenge strings from request metadata (under the assumption that the values being compared against are stable), Anubis now generates random data for each challenge. This data is stored in the active [storage backend](./admin/policies.mdx#storage-backends) for up to 30 minutes. This also fixes [#746](https://github.com/TecharoHQ/anubis/issues/746) and other similar instances of this issue.
|
||||
|
||||
In order to reduce confusion, the "Success" interstitial that shows up when you pass a proof of work challenge has been removed.
|
||||
|
||||
#### Storage
|
||||
|
||||
Anubis now is able to store things persistently [in memory](./admin/policies.mdx#memory), [on the disk](./admin/policies.mdx#bbolt), or [in Valkey](./admin/policies.mdx#valkey) (this includes other compatible software). By default Anubis uses the in-memory backend. If you have an environment with mutable storage (even if it is temporary), be sure to configure the [`bbolt`](./admin/policies.mdx#bbolt) storage backend.
|
||||
|
||||
#### Localization
|
||||
|
||||
Anubis now supports localized responses. Locales can be added in [lib/localization/locales/](https://github.com/TecharoHQ/anubis/tree/main/lib/localization/locales). This release includes support for the following languages:
|
||||
|
||||
- [Brazilian Portugese](https://github.com/TecharoHQ/anubis/pull/726)
|
||||
- [Chinese (Simplified)](https://github.com/TecharoHQ/anubis/pull/774)
|
||||
- [Chinese (Traditional)](https://github.com/TecharoHQ/anubis/pull/759)
|
||||
- English
|
||||
- [Estonian](https://github.com/TecharoHQ/anubis/pull/783)
|
||||
- [Filipino](https://github.com/TecharoHQ/anubis/pull/775)
|
||||
- [French](https://github.com/TecharoHQ/anubis/pull/716)
|
||||
- [German](https://github.com/TecharoHQ/anubis/pull/741)
|
||||
- [Icelandic](https://github.com/TecharoHQ/anubis/pull/780)
|
||||
- [Italian](https://github.com/TecharoHQ/anubis/pull/778)
|
||||
- [Japanese](https://github.com/TecharoHQ/anubis/pull/772)
|
||||
- [Spanish](https://github.com/TecharoHQ/anubis/pull/716)
|
||||
- [Turkish](https://github.com/TecharoHQ/anubis/pull/751)
|
||||
|
||||
If facts or local regulations demand, you can set Anubis default language with the `FORCED_LANGUAGE` environment variable or the `--forced-language` command line argument:
|
||||
|
||||
```sh
|
||||
FORCED_LANGUAGE=de
|
||||
```
|
||||
|
||||
#### Load average
|
||||
|
||||
Anubis can dynamically take action [based on the system load average](./admin/configuration/expressions.mdx#using-the-system-load-average), allowing you to write rules like this:
|
||||
|
||||
```yaml
|
||||
## System load based checks.
|
||||
# If the system is under high load for the last minute, add weight.
|
||||
- name: high-load-average
|
||||
action: WEIGH
|
||||
expression: load_1m >= 10.0 # make sure to end the load comparison in a .0
|
||||
weight:
|
||||
adjust: 20
|
||||
|
||||
# If it is not for the last 15 minutes, remove weight.
|
||||
- name: low-load-average
|
||||
action: WEIGH
|
||||
expression: load_15m <= 4.0 # make sure to end the load comparison in a .0
|
||||
weight:
|
||||
adjust: -10
|
||||
```
|
||||
|
||||
Something to keep in mind about system load average is that it is not aware of the number of cores the system has. If you have a 16 core system that has 16 processes running but none of them is hogging the CPU, then you will get a load average below 16. If you are in doubt, make your "high load" metric at least two times the number of CPU cores and your "low load" metric at least half of the number of CPU cores. For example:
|
||||
|
||||
| Kind | Core count | Load threshold |
|
||||
| --------: | :--------- | :------------- |
|
||||
| high load | 4 | `8.0` |
|
||||
| low load | 4 | `2.0` |
|
||||
| high load | 16 | `32.0` |
|
||||
| low load | 16 | `8` |
|
||||
|
||||
Also keep in mind that this does not account for other kinds of latency like I/O latency. A system can have its web applications unresponsive due to high latency from a MySQL server but still have that web application server report a load near or at zero.
|
||||
|
||||
### Other features and fixes
|
||||
|
||||
There are a bunch of other assorted features and fixes too:
|
||||
|
||||
- Add `COOKIE_SECURE` option to set the cookie [Secure flag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Cookies#block_access_to_your_cookies)
|
||||
- Sets cookie defaults to use [SameSite: None](https://web.dev/articles/samesite-cookies-explained)
|
||||
- Determine the `BIND_NETWORK`/`--bind-network` value from the bind address ([#677](https://github.com/TecharoHQ/anubis/issues/677)).
|
||||
- Implement localization system. Find locale files in lib/localization/locales/.
|
||||
- Implement a [development container](https://containers.dev/) manifest to make contributions easier.
|
||||
- Fix dynamic cookie domains functionality ([#731](https://github.com/TecharoHQ/anubis/pull/731))
|
||||
- Add option for custom cookie prefix ([#732](https://github.com/TecharoHQ/anubis/pull/732))
|
||||
- Add translation for German language ([#741](https://github.com/TecharoHQ/anubis/pull/741))
|
||||
- Remove the "Success" interstitial after a proof of work challenge is concluded.
|
||||
- Anubis now has the concept of [storage backends](./admin/policies.mdx#storage-backends). These allow you to change how Anubis stores temporary data (in memory, on the disk, or in Valkey). If you run Anubis in an environment where you have a low amount of memory available for Anubis (eg: less than 64 megabytes), be sure to configure the [`bbolt`](./admin/policies.mdx#bbolt) storage backend.
|
||||
- The challenge issuance and validation process has been rewritten from scratch. Instead of generating challenge strings from request metadata (under the assumption that the values being compared against are stable), Anubis now generates random data for each challenge. This data is stored in the active [storage backend](./admin/policies.mdx#storage-backends) for up to 30 minutes. Fixes [#564](https://github.com/TecharoHQ/anubis/issues/564), [#746](https://github.com/TecharoHQ/anubis/issues/746), and other similar instances of this issue.
|
||||
- Make the [Open Graph](./admin/configuration/open-graph.mdx) subsystem and DNSBL subsystem use [storage backends](./admin/policies.mdx#storage-backends) instead of storing everything in memory by default.
|
||||
- Add option for forcing a specific language ([#742](https://github.com/TecharoHQ/anubis/pull/742))
|
||||
- Add translation for Turkish language ([#751](https://github.com/TecharoHQ/anubis/pull/751))
|
||||
- Allow [Common Crawl](https://commoncrawl.org/) by default so scrapers have less incentive to scrape
|
||||
- The [bbolt storage backend](./admin/policies.mdx#bbolt) now runs its cleanup every hour instead of every five minutes.
|
||||
- Don't block Anubis starting up if [Thoth](./admin/thoth.mdx) health checks fail.
|
||||
- A race condition involving [opening two challenge pages at once in different tabs](https://github.com/TecharoHQ/anubis/issues/832) causing one of them to fail has been fixed.
|
||||
- The "Try again" button on the error page has been fixed. Previously it meant "try the solution again" instead of "try the challenge again".
|
||||
- In certain cases, a user could be stuck with a test cookie that is invalid, locking them out of the service for up to half an hour. This has been fixed with better validation of this case and clearing the cookie.
|
||||
- Start exposing JA4H fingerprints for later use in CEL expressions.
|
||||
- Add `/healthz` route for use in platform-based health checks.
|
||||
|
||||
### Potentially breaking changes
|
||||
|
||||
We try to introduce breaking changes as much as possible, but these are the changes that may be relevant for you as an administrator:
|
||||
|
||||
#### Challenge format change
|
||||
|
||||
Previously Anubis did no accounting for challenges that it issued. This means that if Anubis restarted during a client, the client would be able to proceed once Anubis came back online.
|
||||
|
||||
During the upgrade to v1.21.0 and when v1.21.0 (or later) restarts with the [in-memory storage backend](./admin/policies.mdx#memory), you may see a higher rate of failed challenges than normal. If this persists beyond a few minutes, [open an issue](https://github.com/TecharoHQ/anubis/issues/new).
|
||||
|
||||
If you are using the in-memory storage backend, please consider using [a different storage backend](./admin/policies.mdx#storage-backends).
|
||||
|
||||
#### Systemd service changes
|
||||
|
||||
The following potentially breaking change applies to native installs with systemd only:
|
||||
|
||||
Each instance of systemd service template now has a unique `RuntimeDirectory`, as opposed to each instance of the service sharing a `RuntimeDirectory`. This change was made to avoid [the `RuntimeDirectory` getting nuked any time one of the Anubis instances restarts](https://github.com/TecharoHQ/anubis/issues/748).
|
||||
|
||||
If you configured Anubis' unix sockets to listen on `/run/anubis/foo.sock` for instance `anubis@foo`, you will need to configure Anubis to listen on `/run/anubis/foo/foo.sock` and additionally configure your HTTP load balancer as appropriate.
|
||||
If you configured Anubis' unix sockets to listen on `/run/anubis/foo.sock` for instance `anubis@foo`, you will need to configure Anubis to listen on `/run/anubis/foo/sock` and additionally configure your HTTP load balancer as appropriate.
|
||||
|
||||
If you need the legacy behaviour, install this [systemd unit dropin](https://www.flatcar.org/docs/latest/setup/systemd/drop-in-units/):
|
||||
|
||||
@@ -193,8 +45,6 @@ If you need the legacy behaviour, install this [systemd unit dropin](https://www
|
||||
RuntimeDirectory=anubis
|
||||
```
|
||||
|
||||
Just keep in mind that this will cause problems when Anubis restarts.
|
||||
|
||||
## v1.20.0: Thancred Waters
|
||||
|
||||
The big ticket items are as follows:
|
||||
|
||||
@@ -77,7 +77,7 @@ For example, consider this rule:
|
||||
|
||||
For this rule, if a request comes in from `8.8.8.8` or `1.1.1.1`, Anubis will deny the request and return an error page.
|
||||
|
||||
### `all` blocks
|
||||
#### `all` blocks
|
||||
|
||||
An `all` block that contains a list of expressions. If all expressions in the list return `true`, then the action specified in the rule will be taken. If any of the expressions in the list returns `false`, Anubis will move on to the next rule.
|
||||
|
||||
@@ -99,18 +99,15 @@ For this rule, if a request comes in matching [the signature of the `go get` com
|
||||
|
||||
Anubis exposes the following variables to expressions:
|
||||
|
||||
| Name | Type | Explanation | Example |
|
||||
| :-------------- | :-------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------- |
|
||||
| `headers` | `map[string, string]` | The [headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers) of the request being processed. | `{"User-Agent": "Mozilla/5.0 Gecko/20100101 Firefox/137.0"}` |
|
||||
| `host` | `string` | The [HTTP hostname](https://web.dev/articles/url-parts#host) the request is targeted to. | `anubis.techaro.lol` |
|
||||
| `load_1m` | `double` | The current system load average over the last one minute. This is useful for making [load-based checks](#using-the-system-load-average). |
|
||||
| `load_5m` | `double` | The current system load average over the last five minutes. This is useful for making [load-based checks](#using-the-system-load-average). |
|
||||
| `load_15m` | `double` | The current system load average over the last fifteen minutes. This is useful for making [load-based checks](#using-the-system-load-average). |
|
||||
| `method` | `string` | The [HTTP method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods) in the request being processed. | `GET`, `POST`, `DELETE`, etc. |
|
||||
| `path` | `string` | The [path](https://web.dev/articles/url-parts#pathname) of the request being processed. | `/`, `/api/memes/create` |
|
||||
| `query` | `map[string, string]` | The [query parameters](https://web.dev/articles/url-parts#query) of the request being processed. | `?foo=bar` -> `{"foo": "bar"}` |
|
||||
| `remoteAddress` | `string` | The IP address of the client. | `1.1.1.1` |
|
||||
| `userAgent` | `string` | The [`User-Agent`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/User-Agent) string in the request being processed. | `Mozilla/5.0 Gecko/20100101 Firefox/137.0` |
|
||||
| Name | Type | Explanation | Example |
|
||||
| :-------------- | :-------------------- | :---------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------- |
|
||||
| `headers` | `map[string, string]` | The [headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers) of the request being processed. | `{"User-Agent": "Mozilla/5.0 Gecko/20100101 Firefox/137.0"}` |
|
||||
| `host` | `string` | The [HTTP hostname](https://web.dev/articles/url-parts#host) the request is targeted to. | `anubis.techaro.lol` |
|
||||
| `method` | `string` | The [HTTP method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods) in the request being processed. | `GET`, `POST`, `DELETE`, etc. |
|
||||
| `path` | `string` | The [path](https://web.dev/articles/url-parts#pathname) of the request being processed. | `/`, `/api/memes/create` |
|
||||
| `query` | `map[string, string]` | The [query parameters](https://web.dev/articles/url-parts#query) of the request being processed. | `?foo=bar` -> `{"foo": "bar"}` |
|
||||
| `remoteAddress` | `string` | The IP address of the client. | `1.1.1.1` |
|
||||
| `userAgent` | `string` | The [`User-Agent`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/User-Agent) string in the request being processed. | `Mozilla/5.0 Gecko/20100101 Firefox/137.0` |
|
||||
|
||||
Of note: in many languages when you look up a key in a map and there is nothing there, the language will return some "falsy" value like `undefined` in JavaScript, `None` in Python, or the zero value of the type in Go. In CEL, if you try to look up a value that does not exist, execution of the expression will fail and Anubis will return an error.
|
||||
|
||||
@@ -123,7 +120,7 @@ In order to avoid this, make sure the header or query parameter you are testing
|
||||
- 'path == "/index.php"'
|
||||
- '"title" in query'
|
||||
- '"action" in query'
|
||||
- 'query["action"] == "history"'
|
||||
- 'query["action"] == "history"
|
||||
```
|
||||
|
||||
This rule throws a challenge if and only if all of the following conditions are true:
|
||||
@@ -144,74 +141,12 @@ X-Real-Ip: 8.8.8.8
|
||||
|
||||
Anubis would return a challenge because all of those conditions are true.
|
||||
|
||||
### Using the system load average
|
||||
|
||||
In Unix-like systems (such as Linux), every process on the system has to wait its turn to be able to run. This means that as more processes on the system are running, they need to wait longer to be able to execute. The [load average](<https://en.wikipedia.org/wiki/Load_(computing)>) represents the number of processes that want to be able to run but can't run yet. This metric isn't the most reliable to identify a cause, but is great at helping to identify symptoms.
|
||||
|
||||
Anubis lets you use the system load average as an input to expressions so that you can make dynamic rules like "when the system is under a low amount of load, dial back the protection, but when it's under a lot of load, crank it up to the mix". This lets you get all of the blocking features of Anubis in the background but only really expose Anubis to users when the system is actively being attacked.
|
||||
|
||||
This is best combined with the [weight](../policies.mdx#request-weight) and [threshold](./thresholds.mdx) systems so that you can have Anubis dynamically respond to attacks. Consider these rules in the default configuration file:
|
||||
|
||||
```yaml
|
||||
## System load based checks.
|
||||
# If the system is under high load for the last minute, add weight.
|
||||
- name: high-load-average
|
||||
action: WEIGH
|
||||
expression: load_1m >= 10.0 # make sure to end the load comparison in a .0
|
||||
weight:
|
||||
adjust: 20
|
||||
|
||||
# If it is not for the last 15 minutes, remove weight.
|
||||
- name: low-load-average
|
||||
action: WEIGH
|
||||
expression: load_15m <= 4.0 # make sure to end the load comparison in a .0
|
||||
weight:
|
||||
adjust: -10
|
||||
```
|
||||
|
||||
This combination of rules makes Anubis dynamically react to the system load and only kick in when the system is under attack.
|
||||
|
||||
Something to keep in mind about system load average is that it is not aware of the number of cores the system has. If you have a 16 core system that has 16 processes running but none of them is hogging the CPU, then you will get a load average below 16. If you are in doubt, make your "high load" metric at least two times the number of CPU cores and your "low load" metric at least half of the number of CPU cores. For example:
|
||||
|
||||
| Kind | Core count | Load threshold |
|
||||
| --------: | :--------- | :------------- |
|
||||
| high load | 4 | `8.0` |
|
||||
| low load | 4 | `2.0` |
|
||||
| high load | 16 | `32.0` |
|
||||
| low load | 16 | `8` |
|
||||
|
||||
Also keep in mind that this does not account for other kinds of latency like I/O latency. A system can have its web applications unresponsive due to high latency from a MySQL server but still have that web application server report a load near or at zero.
|
||||
|
||||
## Functions exposed to Anubis expressions
|
||||
|
||||
Anubis expressions can be augmented with the following functions:
|
||||
|
||||
### `missingHeader`
|
||||
|
||||
Available in `bot` expressions.
|
||||
|
||||
```ts
|
||||
function missingHeader(headers: Record<string, string>, key: string) bool
|
||||
```
|
||||
|
||||
`missingHeader` returns `true` if the request does not contain a header. This is useful when you are trying to assert behavior such as:
|
||||
|
||||
```yaml
|
||||
# Adds weight to old versions of Chrome
|
||||
- name: old-chrome
|
||||
action: WEIGH
|
||||
weight:
|
||||
adjust: 10
|
||||
expression:
|
||||
all:
|
||||
- userAgent.matches("Chrome/[1-9][0-9]?\\.0\\.0\\.0")
|
||||
- missingHeader(headers, "Sec-Ch-Ua")
|
||||
```
|
||||
|
||||
### `randInt`
|
||||
|
||||
Available in all expressions.
|
||||
|
||||
```ts
|
||||
function randInt(n: int): int;
|
||||
```
|
||||
|
||||
@@ -30,7 +30,7 @@ Effectively you have one trip through Apache to do TLS termination, a detour thr
|
||||
|
||||
:::note
|
||||
|
||||
These examples assume that you are using a setup where your Apache configuration is made up of a bunch of files in `/etc/httpd/conf.d/*.conf`. This is not true for all deployments of Apache. If you are not in such an environment, append these snippets to your `/etc/httpd/conf/httpd.conf` file.
|
||||
These examples assume that you are using a setup where your nginx configuration is made up of a bunch of files in `/etc/httpd/conf.d/*.conf`. This is not true for all deployments of Apache. If you are not in such an environment, append these snippets to your `/etc/httpd/conf/httpd.conf` file.
|
||||
|
||||
:::
|
||||
|
||||
@@ -56,7 +56,6 @@ Assuming you are protecting `anubistest.techaro.lol`, you need the following ser
|
||||
</VirtualHost>
|
||||
|
||||
# HTTPS listener that forwards to Anubis
|
||||
<IfModule mod_proxy.c>
|
||||
<VirtualHost *:443>
|
||||
ServerAdmin your@email.here
|
||||
ServerName anubistest.techaro.lol
|
||||
|
||||
@@ -4,7 +4,7 @@ Docker compose is typically used in concert with other load balancers such as [A
|
||||
|
||||
```yaml
|
||||
services:
|
||||
anubis:
|
||||
anubis-nginx:
|
||||
image: ghcr.io/techarohq/anubis:latest
|
||||
environment:
|
||||
BIND: ":8080"
|
||||
@@ -15,17 +15,10 @@ services:
|
||||
POLICY_FNAME: "/data/cfg/botPolicy.yaml"
|
||||
OG_PASSTHROUGH: "true"
|
||||
OG_EXPIRY_TIME: "24h"
|
||||
healthcheck:
|
||||
test: ["CMD", "anubis", "--healthcheck"]
|
||||
interval: 5s
|
||||
timeout: 30s
|
||||
retries: 5
|
||||
start_period: 500ms
|
||||
ports:
|
||||
- 8080:8080
|
||||
volumes:
|
||||
- "./botPolicy.yaml:/data/cfg/botPolicy.yaml:ro"
|
||||
|
||||
nginx:
|
||||
image: nginx
|
||||
volumes:
|
||||
|
||||
@@ -79,10 +79,6 @@ server {
|
||||
root "/srv/http/anubistest.techaro.lol";
|
||||
index index.html;
|
||||
|
||||
# Get the visiting IP from the TLS termination server
|
||||
set_real_ip_from unix:;
|
||||
real_ip_header X-Real-IP;
|
||||
|
||||
# Your normal configuration can go here
|
||||
# location .php { fastcgi...} etc.
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ Anubis uses these environment variables for configuration:
|
||||
| `COOKIE_PARTITIONED` | `false` | If set to `true`, enables the [partitioned (CHIPS) flag](https://developers.google.com/privacy-sandbox/cookies/chips), meaning that Anubis inside an iframe has a different set of cookies than the domain hosting the iframe. |
|
||||
| `COOKIE_SECURE` | `true` | If set to `true`, enables the [Secure flag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Cookies#block_access_to_your_cookies), meaning that the cookies will only be transmitted over HTTPS. If Anubis is used in an unsecure context (plain HTTP), this will be need to be set to false |
|
||||
| `DIFFICULTY` | `4` | The difficulty of the challenge, or the number of leading zeroes that must be in successful responses. |
|
||||
| `ED25519_PRIVATE_KEY_HEX` | unset | The hex-encoded ed25519 private key used to sign Anubis responses. If this is not set, Anubis will generate one for you. This should be exactly 64 characters long. When running multiple instances on the same base domain, the key must be the same across all instances. See below for details. |
|
||||
| `ED25519_PRIVATE_KEY_HEX` | unset | The hex-encoded ed25519 private key used to sign Anubis responses. If this is not set, Anubis will generate one for you. This should be exactly 64 characters long. See below for details. |
|
||||
| `ED25519_PRIVATE_KEY_HEX_FILE` | unset | Path to a file containing the hex-encoded ed25519 private key. Only one of this or its sister option may be set. |
|
||||
| `METRICS_BIND` | `:9090` | The network address that Anubis serves Prometheus metrics on. See `BIND` for more information. |
|
||||
| `METRICS_BIND_NETWORK` | `tcp` | The address family that the Anubis metrics server listens on. See `BIND_NETWORK` for more information. |
|
||||
|
||||
@@ -137,7 +137,7 @@ Test to make sure it's running with `curl`:
|
||||
curl http://localhost:8240/metrics
|
||||
```
|
||||
|
||||
Then set up your reverse proxy (Nginx, Caddy, etc.) to point to the Anubis port. Anubis will then reverse proxy all requests that meet the policies in `/etc/anubis/gitea.botPolicies.yaml` to the target service.
|
||||
Then set up your reverse proxy (Nginx, Caddy, etc.) to point to the Anubis port. Anubis will then reverse proxy all requests that meet the policies in `/etc/anubis/gitea.botPolicies.json` to the target service.
|
||||
|
||||
For more details on particular reverse proxies, see here:
|
||||
|
||||
|
||||
@@ -268,12 +268,6 @@ The memory backend is an in-memory cache. This backend works best if you don't u
|
||||
|
||||
The biggest downside is that there is not currently a limit to how much data can be stored in memory. This will be addressed at a later time.
|
||||
|
||||
:::warning
|
||||
|
||||
The in-memory backend exists mostly for validation, testing, and to ensure that the default configuration of Anubis works as expected. Do not use this persistently in production.
|
||||
|
||||
:::
|
||||
|
||||
#### Configuration
|
||||
|
||||
The memory backend does not require any configuration to use.
|
||||
@@ -295,9 +289,10 @@ When Anubis opens a bbolt database, it takes an exclusive lock on that database.
|
||||
|
||||
The `bbolt` backend takes the following configuration options:
|
||||
|
||||
| Name | Type | Example | Description |
|
||||
| :----- | :--- | :----------------- | :--------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `path` | path | `/data/anubis.bdb` | The filesystem path for the Anubis bbolt database. Anubis requires write access to the folder containing the bbolt database. |
|
||||
| Name | Type | Example | Description |
|
||||
| :------- | :----- | :----------------- | :-------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `bucket` | string | `anubis` | The bbolt bucket that Anubis should place all its data into. If this is not set, then Anubis will default to the bucket `anubis`. |
|
||||
| `path` | path | `/data/anubis.bdb` | The filesystem path for the Anubis bbolt database. Anubis requires write access to the folder containing the bbolt database. |
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
@@ -58,20 +58,6 @@ Anubis is brought to you by sponsors and donors like:
|
||||
<a href="https://wildbase.xyz/">
|
||||
<img src="/img/sponsors/wildbase-logo.webp" alt="Wildbase" height="64" />
|
||||
</a>
|
||||
<a href="https://emma.pet">
|
||||
<img
|
||||
src="/img/sponsors/nepeat-logo.webp"
|
||||
alt="Cat eyes over the word Emma in a serif font"
|
||||
height="64"
|
||||
/>
|
||||
</a>
|
||||
<a href="https://fabulous.systems/">
|
||||
<img
|
||||
src="/img/sponsors/fabulous-systems.webp"
|
||||
alt="Cat eyes over the word Emma in a serif font"
|
||||
height="64"
|
||||
/>
|
||||
</a>
|
||||
|
||||
## Overview
|
||||
|
||||
|
||||
@@ -50,14 +50,6 @@ This page contains a non-exhaustive list with all websites using Anubis.
|
||||
- https://coinhoards.org/
|
||||
- https://pluralpedia.org/
|
||||
- https://git.aya.so/
|
||||
- https://marginalia-search.com/
|
||||
- https://repositorio.ufrn.br/home/
|
||||
- https://mozillazine.org/
|
||||
- https://clew.se/
|
||||
- https://tumfatig.net/
|
||||
- https://rpmfusion.org/
|
||||
- https://wiki.freepascal.org/
|
||||
- https://azurlane.koumakan.jp/
|
||||
- <details>
|
||||
<summary>FreeCAD</summary>
|
||||
- https://forum.freecad.org/
|
||||
@@ -102,14 +94,3 @@ This page contains a non-exhaustive list with all websites using Anubis.
|
||||
- https://find.library.duke.edu/
|
||||
- https://nicholas.duke.edu/
|
||||
</details>
|
||||
- <details>
|
||||
<summary>Forschungszentrum Jülich</summary>
|
||||
- https://juser.fz-juelich.de/
|
||||
</details>
|
||||
- <details>
|
||||
<summary>archlinux32.org</summary>
|
||||
- https://www.archlinux32.org/packages/
|
||||
- https://bbs.archlinux32.org/
|
||||
- https://bugs.archlinux32.org/
|
||||
</details>
|
||||
|
||||
|
||||
@@ -139,10 +139,6 @@ const config: Config = {
|
||||
label: 'GitHub',
|
||||
href: 'https://github.com/TecharoHQ/anubis',
|
||||
},
|
||||
{
|
||||
label: 'Status',
|
||||
href: 'https://techarohq.github.io/status/'
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
app = 'anubis-docs'
|
||||
primary_region = 'yyz'
|
||||
|
||||
[build]
|
||||
image = "ghcr.io/techarohq/anubis/docs:main"
|
||||
|
||||
[http_service]
|
||||
internal_port = 80
|
||||
force_https = true
|
||||
auto_stop_machines = true
|
||||
auto_start_machines = true
|
||||
min_machines_running = 0
|
||||
processes = ['app']
|
||||
|
||||
[[vm]]
|
||||
cpu_kind = 'shared'
|
||||
cpus = 1
|
||||
memory_mb = 256
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
|
||||
types {
|
||||
text/html html htm shtml;
|
||||
text/css css;
|
||||
text/xml xml;
|
||||
image/gif gif;
|
||||
image/jpeg jpeg jpg;
|
||||
application/javascript js;
|
||||
application/atom+xml atom;
|
||||
application/rss+xml rss;
|
||||
|
||||
text/mathml mml;
|
||||
text/plain txt;
|
||||
text/vnd.sun.j2me.app-descriptor jad;
|
||||
text/vnd.wap.wml wml;
|
||||
text/x-component htc;
|
||||
|
||||
image/avif avif;
|
||||
image/png png;
|
||||
image/svg+xml svg svgz;
|
||||
image/tiff tif tiff;
|
||||
image/vnd.wap.wbmp wbmp;
|
||||
image/webp webp;
|
||||
image/x-icon ico;
|
||||
image/x-jng jng;
|
||||
image/x-ms-bmp bmp;
|
||||
|
||||
font/woff woff;
|
||||
font/woff2 woff2;
|
||||
|
||||
application/java-archive jar war ear;
|
||||
application/json json;
|
||||
application/mac-binhex40 hqx;
|
||||
application/msword doc;
|
||||
application/pdf pdf;
|
||||
application/postscript ps eps ai;
|
||||
application/rtf rtf;
|
||||
application/vnd.apple.mpegurl m3u8;
|
||||
application/vnd.google-earth.kml+xml kml;
|
||||
application/vnd.google-earth.kmz kmz;
|
||||
application/vnd.ms-excel xls;
|
||||
application/vnd.ms-fontobject eot;
|
||||
application/vnd.ms-powerpoint ppt;
|
||||
application/vnd.oasis.opendocument.graphics odg;
|
||||
application/vnd.oasis.opendocument.presentation odp;
|
||||
application/vnd.oasis.opendocument.spreadsheet ods;
|
||||
application/vnd.oasis.opendocument.text odt;
|
||||
application/vnd.openxmlformats-officedocument.presentationml.presentation
|
||||
pptx;
|
||||
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||
xlsx;
|
||||
application/vnd.openxmlformats-officedocument.wordprocessingml.document
|
||||
docx;
|
||||
application/vnd.wap.wmlc wmlc;
|
||||
application/wasm wasm;
|
||||
application/x-7z-compressed 7z;
|
||||
application/x-cocoa cco;
|
||||
application/x-java-archive-diff jardiff;
|
||||
application/x-java-jnlp-file jnlp;
|
||||
application/x-makeself run;
|
||||
application/x-perl pl pm;
|
||||
application/x-pilot prc pdb;
|
||||
application/x-rar-compressed rar;
|
||||
application/x-redhat-package-manager rpm;
|
||||
application/x-sea sea;
|
||||
application/x-shockwave-flash swf;
|
||||
application/x-stuffit sit;
|
||||
application/x-tcl tcl tk;
|
||||
application/x-x509-ca-cert der pem crt;
|
||||
application/x-xpinstall xpi;
|
||||
application/xhtml+xml xhtml;
|
||||
application/xspf+xml xspf;
|
||||
application/zip zip;
|
||||
|
||||
application/octet-stream bin exe dll;
|
||||
application/octet-stream deb;
|
||||
application/octet-stream dmg;
|
||||
application/octet-stream iso img;
|
||||
application/octet-stream msi msp msm;
|
||||
|
||||
audio/midi mid midi kar;
|
||||
audio/mpeg mp3;
|
||||
audio/ogg ogg;
|
||||
audio/x-m4a m4a;
|
||||
audio/x-realaudio ra;
|
||||
|
||||
video/3gpp 3gpp 3gp;
|
||||
video/mp2t ts;
|
||||
video/mp4 mp4;
|
||||
video/mpeg mpeg mpg;
|
||||
video/quicktime mov;
|
||||
video/webm webm;
|
||||
video/x-flv flv;
|
||||
video/x-m4v m4v;
|
||||
video/x-mng mng;
|
||||
video/x-ms-asf asx asf;
|
||||
video/x-ms-wmv wmv;
|
||||
video/x-msvideo avi;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
user nginx;
|
||||
worker_processes 2;
|
||||
error_log /dev/stdout warn;
|
||||
pid /nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
access_log /dev/stdout;
|
||||
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
|
||||
server {
|
||||
listen 80 default_server;
|
||||
server_name _;
|
||||
|
||||
error_page 404 /404.html;
|
||||
|
||||
root /www;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,9 +15,6 @@ spec:
|
||||
- name: anubis
|
||||
configMap:
|
||||
name: anubis-cfg
|
||||
- name: nginx
|
||||
configMap:
|
||||
name: nginx-cfg
|
||||
- name: temporary-data
|
||||
emptyDir: {}
|
||||
containers:
|
||||
@@ -31,23 +28,8 @@ spec:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 128Mi
|
||||
volumeMounts:
|
||||
- name: nginx
|
||||
mountPath: /conf
|
||||
ports:
|
||||
- containerPort: 80
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
initialDelaySeconds: 1
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 20
|
||||
- name: anubis
|
||||
image: ghcr.io/techarohq/anubis:main
|
||||
imagePullPolicy: Always
|
||||
@@ -93,15 +75,3 @@ spec:
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: anubis-docs-thoth
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 9090
|
||||
initialDelaySeconds: 1
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 9090
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 20
|
||||
|
||||
@@ -11,8 +11,3 @@ configMapGenerator:
|
||||
behavior: create
|
||||
files:
|
||||
- ./cfg/anubis/botPolicies.yaml
|
||||
- name: nginx-cfg
|
||||
behavior: create
|
||||
files:
|
||||
- ./cfg/nginx/mime.types
|
||||
- ./cfg/nginx/nginx.conf
|
||||
|
||||
BIN
docs/static/img/sponsors/fabulous-systems.webp
vendored
BIN
docs/static/img/sponsors/fabulous-systems.webp
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 5.0 KiB |
BIN
docs/static/img/sponsors/nepeat-logo.webp
vendored
BIN
docs/static/img/sponsors/nepeat-logo.webp
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 4.4 KiB |
36
go.mod
36
go.mod
@@ -7,24 +7,21 @@ require (
|
||||
github.com/a-h/templ v0.3.906
|
||||
github.com/cespare/xxhash/v2 v2.3.0
|
||||
github.com/facebookgo/flagenv v0.0.0-20160425205200-fcd59fca7456
|
||||
github.com/gaissmai/bart v0.20.5
|
||||
github.com/gaissmai/bart v0.20.4
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2
|
||||
github.com/google/cel-go v0.25.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/lum8rjack/go-ja4h v0.0.0-20250606032308-3a989c6635be
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.0
|
||||
github.com/playwright-community/playwright-go v0.5200.0
|
||||
github.com/prometheus/client_golang v1.22.0
|
||||
github.com/redis/go-redis/v9 v9.11.0
|
||||
github.com/sebest/xff v0.0.0-20210106013422-671bd2870b3a
|
||||
github.com/shirou/gopsutil/v4 v4.25.6
|
||||
github.com/testcontainers/testcontainers-go v0.37.0
|
||||
go.etcd.io/bbolt v1.4.2
|
||||
golang.org/x/net v0.42.0
|
||||
golang.org/x/text v0.27.0
|
||||
golang.org/x/net v0.41.0
|
||||
golang.org/x/text v0.26.0
|
||||
google.golang.org/grpc v1.73.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/apimachinery v0.33.2
|
||||
@@ -70,7 +67,7 @@ require (
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dop251/goja v0.0.0-20250309171923-bcd7cc6bf64c // indirect
|
||||
github.com/ebitengine/purego v0.8.4 // indirect
|
||||
github.com/ebitengine/purego v0.8.2 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
|
||||
@@ -82,7 +79,7 @@ require (
|
||||
github.com/go-git/go-billy/v5 v5.6.2 // indirect
|
||||
github.com/go-git/go-git/v5 v5.14.0 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.4 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
|
||||
@@ -130,6 +127,7 @@ require (
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.25.1 // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/skeema/knownhosts v1.3.1 // indirect
|
||||
@@ -140,6 +138,7 @@ require (
|
||||
github.com/suzuki-shunsuke/logrus-error v0.1.4 // indirect
|
||||
github.com/suzuki-shunsuke/pinact v1.6.0 // indirect
|
||||
github.com/suzuki-shunsuke/urfave-cli-help-all v0.0.4 // indirect
|
||||
github.com/testcontainers/testcontainers-go v0.37.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/ulikunitz/xz v0.5.12 // indirect
|
||||
@@ -150,28 +149,25 @@ require (
|
||||
gitlab.com/digitalxero/go-conventional-commit v1.0.7 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.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.37.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.37.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.7.0 // indirect
|
||||
go.opentelemetry.io/otel v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.3 // indirect
|
||||
golang.org/x/crypto v0.40.0 // indirect
|
||||
golang.org/x/crypto v0.39.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 // indirect
|
||||
golang.org/x/mod v0.25.0 // indirect
|
||||
golang.org/x/oauth2 v0.28.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
golang.org/x/sync v0.15.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7 // indirect
|
||||
golang.org/x/term v0.33.0 // indirect
|
||||
golang.org/x/term v0.32.0 // indirect
|
||||
golang.org/x/tools v0.34.0 // indirect
|
||||
golang.org/x/vuln v1.1.4 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
honnef.co/go/tools v0.6.1 // indirect
|
||||
|
||||
82
go.sum
82
go.sum
@@ -6,8 +6,6 @@ cel.dev/expr v0.23.1 h1:K4KOtPCJQjVggkARsjG9RWXP6O4R73aHeJMa/dmCQQg=
|
||||
cel.dev/expr v0.23.1/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
|
||||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w=
|
||||
github.com/AlekSi/pointer v1.2.0/go.mod h1:gZGfd3dpW4vEc/UlyfKKi1roIqcCgwOIvb0tSNSBle0=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
@@ -109,8 +107,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/dop251/goja v0.0.0-20250309171923-bcd7cc6bf64c h1:mxWGS0YyquJ/ikZOjSrRjjFIbUqIP9ojyYQ+QZTU3Rg=
|
||||
github.com/dop251/goja v0.0.0-20250309171923-bcd7cc6bf64c/go.mod h1:MxLav0peU43GgvwVgNbLAj1s/bSGboKkhuULvq/7hx4=
|
||||
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
|
||||
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
|
||||
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
@@ -133,8 +131,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
||||
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/gaissmai/bart v0.20.5 h1:ehoWZWQ7j//qt0K0Zs4i9hpoPpbgqsMQiR8W2QPJh+c=
|
||||
github.com/gaissmai/bart v0.20.5/go.mod h1:cEed+ge8dalcbpi8wtS9x9m2hn/fNJH5suhdGQOHnYk=
|
||||
github.com/gaissmai/bart v0.20.4 h1:Ik47r1fy3jRVU+1eYzKSW3ho2UgBVTVnUS8O993584U=
|
||||
github.com/gaissmai/bart v0.20.4/go.mod h1:cEed+ge8dalcbpi8wtS9x9m2hn/fNJH5suhdGQOHnYk=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
@@ -148,8 +146,8 @@ github.com/go-git/go-git/v5 v5.14.0/go.mod h1:Z5Xhoia5PcWA3NF8vRLURn9E5FRhSl7dGj
|
||||
github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY=
|
||||
github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
@@ -216,8 +214,6 @@ github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
|
||||
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
@@ -253,8 +249,6 @@ github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/lum8rjack/go-ja4h v0.0.0-20250606032308-3a989c6635be h1:dVIND0nXGXPQnFZYrMXT6CxHhBYhTPMm0GFqcmfaIC4=
|
||||
github.com/lum8rjack/go-ja4h v0.0.0-20250606032308-3a989c6635be/go.mod h1:q68TUR45WDa2r3yU4aO6WgxfCc0Vj1qtRaKaRE3yMLM=
|
||||
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
|
||||
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
|
||||
@@ -335,8 +329,8 @@ github.com/sebest/xff v0.0.0-20210106013422-671bd2870b3a h1:iLcLb5Fwwz7g/DLK89F+
|
||||
github.com/sebest/xff v0.0.0-20210106013422-671bd2870b3a/go.mod h1:wozgYq9WEBQBaIJe4YZ0qTSFAMxmcwBhQH0fO0R34Z0=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs=
|
||||
github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
|
||||
github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs=
|
||||
github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI=
|
||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
@@ -357,8 +351,6 @@ github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8w
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
@@ -403,22 +395,16 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
|
||||
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.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 v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
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.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||
go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE=
|
||||
@@ -429,8 +415,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 h1:1P7xPZEwZMoBoz0Yze5Nx2/4pxj6nw9ZqHWXqP0iRgQ=
|
||||
@@ -450,8 +436,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
|
||||
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -459,8 +445,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -487,8 +473,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7 h1:FemxDzfMUcK2f3YY4H+05K9CDzbSVr2+q/JKN45pey0=
|
||||
golang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
@@ -496,8 +482,8 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
|
||||
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
|
||||
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@@ -506,10 +492,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
@@ -527,10 +511,10 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 h1:LLhsEBxRTBLuKlQxFBYUOU8xyFgXv6cOTp2HASDlsDk=
|
||||
golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a h1:SGktgSolFCo75dnHJF2yMvnns6jCmHFJ0vE4Vn2JKvQ=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a/go.mod h1:a77HrdMjoeKbnd2jmgcWdaS++ZLZAEq3orIOAEIKiVw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 h1:hE3bRWtU6uceqlh4fhrSnUyjKHMKB9KrTLLG+bc0ddM=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463/go.mod h1:U90ffi8eUL9MwPcrJylN5+Mk2v3vuPDptd5yyNUiRR8=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
@@ -549,8 +533,6 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||
honnef.co/go/tools v0.6.1 h1:R094WgE8K4JirYjBaOpz/AvTyUu/3wbmAoskKN/pxTI=
|
||||
honnef.co/go/tools v0.6.1/go.mod h1:3puzxxljPCe8RGJX7BIy1plGbxEOZni5mR2aXe3/uk4=
|
||||
k8s.io/apimachinery v0.33.2 h1:IHFVhqg59mb8PJWTLi8m1mAoepkUNYmptHsV+Z1m5jY=
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/grpc/health"
|
||||
healthv1 "google.golang.org/grpc/health/grpc_health_v1"
|
||||
)
|
||||
|
||||
var HealthSrv = health.NewServer()
|
||||
|
||||
func SetHealth(svc string, status healthv1.HealthCheckResponse_ServingStatus) {
|
||||
HealthSrv.SetServingStatus(svc, status)
|
||||
}
|
||||
|
||||
func GetHealth(svc string) (healthv1.HealthCheckResponse_ServingStatus, bool) {
|
||||
st, err := HealthSrv.Check(context.Background(), &healthv1.HealthCheckRequest{
|
||||
Service: svc,
|
||||
})
|
||||
if err != nil {
|
||||
return healthv1.HealthCheckResponse_UNKNOWN, false
|
||||
}
|
||||
|
||||
return st.GetStatus(), true
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/lum8rjack/go-ja4h"
|
||||
)
|
||||
|
||||
func JA4H(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
r.Header.Add("X-Http-Fingerprint-JA4H", ja4h.JA4H(r))
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
@@ -60,6 +60,15 @@ func New(ctx context.Context, thothURL, apiToken string, plaintext bool) (*Clien
|
||||
|
||||
hc := healthv1.NewHealthClient(conn)
|
||||
|
||||
resp, err := hc.Check(ctx, &healthv1.HealthCheckRequest{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't verify thoth health at %s: %w", thothURL, err)
|
||||
}
|
||||
|
||||
if resp.Status != healthv1.HealthCheckResponse_SERVING {
|
||||
return nil, fmt.Errorf("thoth is not healthy, wanted %s but got %s", healthv1.HealthCheckResponse_SERVING, resp.Status)
|
||||
}
|
||||
|
||||
return &Client{
|
||||
conn: conn,
|
||||
health: hc,
|
||||
|
||||
@@ -102,10 +102,6 @@ func (s *Server) challengeFor(r *http.Request) (*challenge.Challenge, error) {
|
||||
ckie := ckies[0]
|
||||
chall, err := j.Get(r.Context(), "challenge:"+ckie.Value)
|
||||
if err != nil {
|
||||
if errors.Is(err, store.ErrNotFound) {
|
||||
return s.issueChallenge(r.Context(), r)
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -384,23 +380,6 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) {
|
||||
lg := internal.GetRequestLogger(r)
|
||||
localizer := localization.GetLocalizer(r)
|
||||
|
||||
redir := r.FormValue("redir")
|
||||
redirURL, err := url.ParseRequestURI(redir)
|
||||
if err != nil {
|
||||
lg.Error("invalid redirect", "err", err)
|
||||
s.respondWithStatus(w, r, localizer.T("invalid_redirect"), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
switch redirURL.Scheme {
|
||||
case "", "http", "https":
|
||||
// allowed
|
||||
default:
|
||||
lg.Error("XSS attempt blocked, invalid redirect scheme", "scheme", redirURL.Scheme)
|
||||
s.respondWithStatus(w, r, localizer.T("invalid_redirect"), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Adjust cookie path if base prefix is not empty
|
||||
cookiePath := "/"
|
||||
if anubis.BasePrefix != "" {
|
||||
@@ -415,6 +394,15 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
s.ClearCookie(w, CookieOpts{Name: anubis.TestCookieName, Host: r.Host})
|
||||
|
||||
redir := r.FormValue("redir")
|
||||
redirURL, err := url.ParseRequestURI(redir)
|
||||
if err != nil {
|
||||
lg.Error("invalid redirect", "err", err)
|
||||
s.respondWithError(w, r, localizer.T("invalid_redirect"))
|
||||
return
|
||||
}
|
||||
// used by the path checker rule
|
||||
r.URL = redirURL
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
@@ -738,230 +736,3 @@ func TestStripBasePrefixFromRequest(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestChallengeFor_ErrNotFound makes sure that users with invalid challenge IDs
|
||||
// in the test cookie don't get rejected by the database lookup failing.
|
||||
func TestChallengeFor_ErrNotFound(t *testing.T) {
|
||||
pol := loadPolicies(t, "testdata/aggressive_403.yaml", 0)
|
||||
ckieExpiration := 10 * time.Minute
|
||||
const wrongCookie = "wrong cookie"
|
||||
|
||||
srv := spawnAnubis(t, Options{
|
||||
Next: http.NewServeMux(),
|
||||
Policy: pol,
|
||||
|
||||
CookieDomain: "127.0.0.1",
|
||||
CookieExpiration: ckieExpiration,
|
||||
})
|
||||
|
||||
req := httptest.NewRequest("GET", "http://example.com/", nil)
|
||||
req.Header.Set("X-Real-IP", "127.0.0.1")
|
||||
req.Header.Set("User-Agent", "CHALLENGE")
|
||||
req.AddCookie(&http.Cookie{Name: anubis.TestCookieName, Value: wrongCookie})
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
srv.maybeReverseProxyOrPage(w, req)
|
||||
|
||||
resp := w.Result()
|
||||
defer resp.Body.Close()
|
||||
|
||||
body := new(strings.Builder)
|
||||
_, err := io.Copy(body, resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("reading body should not fail: %v", err)
|
||||
}
|
||||
|
||||
t.Run("make sure challenge page is issued", func(t *testing.T) {
|
||||
if !strings.Contains(body.String(), "anubis_challenge") {
|
||||
t.Error("should get a challenge page")
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusUnauthorized {
|
||||
t.Errorf("should get a 401 Unauthorized, got: %d", resp.StatusCode)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("make sure that the body is not an error page", func(t *testing.T) {
|
||||
if strings.Contains(body.String(), "reject.webp") {
|
||||
t.Error("should not get an internal server error")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("make sure new test cookie is issued", func(t *testing.T) {
|
||||
found := false
|
||||
for _, cookie := range resp.Cookies() {
|
||||
if cookie.Name == anubis.TestCookieName {
|
||||
if cookie.Value == wrongCookie {
|
||||
t.Error("a new challenge cookie should be issued")
|
||||
}
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Error("a new test cookie should be set")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestPassChallengeXSS(t *testing.T) {
|
||||
pol := loadPolicies(t, "", anubis.DefaultDifficulty)
|
||||
|
||||
srv := spawnAnubis(t, Options{
|
||||
Next: http.NewServeMux(),
|
||||
Policy: pol,
|
||||
})
|
||||
|
||||
ts := httptest.NewServer(internal.RemoteXRealIP(true, "tcp", srv))
|
||||
defer ts.Close()
|
||||
|
||||
cli := httpClient(t)
|
||||
chall := makeChallenge(t, ts, cli)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
redir string
|
||||
}{
|
||||
{
|
||||
name: "javascript alert",
|
||||
redir: "javascript:alert('xss')",
|
||||
},
|
||||
{
|
||||
name: "vbscript",
|
||||
redir: "vbscript:msgbox(\"XSS\")",
|
||||
},
|
||||
{
|
||||
name: "data url",
|
||||
redir: "data:text/html;base64,PHNjcmlwdD5hbGVydCgneHNzJyk8L3NjcmlwdD4=",
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("with test cookie", func(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
nonce := 0
|
||||
elapsedTime := 420
|
||||
calculated := ""
|
||||
calcString := fmt.Sprintf("%s%d", chall.Challenge, nonce)
|
||||
calculated = internal.SHA256sum(calcString)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, ts.URL+"/.within.website/x/cmd/anubis/api/pass-challenge", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("can't make request: %v", err)
|
||||
}
|
||||
|
||||
q := req.URL.Query()
|
||||
q.Set("response", calculated)
|
||||
q.Set("nonce", fmt.Sprint(nonce))
|
||||
q.Set("redir", tc.redir)
|
||||
q.Set("elapsedTime", fmt.Sprint(elapsedTime))
|
||||
req.URL.RawQuery = q.Encode()
|
||||
|
||||
u, err := url.Parse(ts.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, ckie := range cli.Jar.Cookies(u) {
|
||||
if ckie.Name == anubis.TestCookieName {
|
||||
req.AddCookie(ckie)
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := cli.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("can't do request: %v", err)
|
||||
}
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
|
||||
if bytes.Contains(body, []byte(tc.redir)) {
|
||||
t.Log(string(body))
|
||||
t.Error("found XSS in HTML body")
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusBadRequest {
|
||||
t.Errorf("wanted status %d, got %d. body: %s", http.StatusBadRequest, resp.StatusCode, body)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("no test cookie", func(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
nonce := 0
|
||||
elapsedTime := 420
|
||||
calculated := ""
|
||||
calcString := fmt.Sprintf("%s%d", chall.Challenge, nonce)
|
||||
calculated = internal.SHA256sum(calcString)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, ts.URL+"/.within.website/x/cmd/anubis/api/pass-challenge", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("can't make request: %v", err)
|
||||
}
|
||||
|
||||
q := req.URL.Query()
|
||||
q.Set("response", calculated)
|
||||
q.Set("nonce", fmt.Sprint(nonce))
|
||||
q.Set("redir", tc.redir)
|
||||
q.Set("elapsedTime", fmt.Sprint(elapsedTime))
|
||||
req.URL.RawQuery = q.Encode()
|
||||
|
||||
resp, err := cli.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("can't do request: %v", err)
|
||||
}
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
|
||||
if bytes.Contains(body, []byte(tc.redir)) {
|
||||
t.Log(string(body))
|
||||
t.Error("found XSS in HTML body")
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusBadRequest {
|
||||
t.Errorf("wanted status %d, got %d. body: %s", http.StatusBadRequest, resp.StatusCode, body)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestXForwardedForNoDoubleComma(t *testing.T) {
|
||||
var h http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("X-Forwarded-For", r.Header.Get("X-Forwarded-For"))
|
||||
fmt.Fprintln(w, "OK")
|
||||
})
|
||||
|
||||
h = internal.XForwardedForToXRealIP(h)
|
||||
h = internal.XForwardedForUpdate(false, h)
|
||||
|
||||
pol := loadPolicies(t, "testdata/permissive.yaml", 4)
|
||||
|
||||
srv := spawnAnubis(t, Options{
|
||||
Next: h,
|
||||
Policy: pol,
|
||||
})
|
||||
ts := httptest.NewServer(srv)
|
||||
t.Cleanup(ts.Close)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
req.Header.Set("X-Real-Ip", "10.0.0.1")
|
||||
|
||||
resp, err := ts.Client().Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Errorf("response status is wrong, wanted %d but got: %s", http.StatusOK, resp.Status)
|
||||
}
|
||||
|
||||
if xff := resp.Header.Get("X-Forwarded-For"); strings.HasPrefix(xff, ",,") {
|
||||
t.Errorf("X-Forwarded-For has two leading commas: %q", xff)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +131,6 @@ func (s *Server) RenderIndex(w http.ResponseWriter, r *http.Request, rule *polic
|
||||
chall, err := s.challengeFor(r)
|
||||
if err != nil {
|
||||
lg.Error("can't get challenge", "err", "err")
|
||||
s.ClearCookie(w, CookieOpts{Name: anubis.TestCookieName, Host: r.Host})
|
||||
s.respondWithError(w, r, fmt.Sprintf("%s: %s", localizer.T("internal_server_error"), rule.Challenge.Algorithm))
|
||||
return
|
||||
}
|
||||
@@ -156,7 +155,6 @@ func (s *Server) RenderIndex(w http.ResponseWriter, r *http.Request, rule *polic
|
||||
impl, ok := challenge.Get(rule.Challenge.Algorithm)
|
||||
if !ok {
|
||||
lg.Error("check failed", "err", "can't get algorithm", "algorithm", rule.Challenge.Algorithm)
|
||||
s.ClearCookie(w, CookieOpts{Name: anubis.TestCookieName, Host: r.Host})
|
||||
s.respondWithError(w, r, fmt.Sprintf("%s: %s", localizer.T("internal_server_error"), rule.Challenge.Algorithm))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
{
|
||||
"loading": "Načítám...",
|
||||
"why_am_i_seeing": "Proč to vidím?",
|
||||
"protected_by": "Chráněno pomocí",
|
||||
"protected_from": "Od",
|
||||
"made_with": "Vytvořeno s ❤️ v 🇨🇦",
|
||||
"mascot_design": "Design maskota od",
|
||||
"ai_companies_explanation": "Vidíte to proto, že správce této webové stránky nastavil Anubis na ochranu serveru před pohromou AI společností agresivně stahujících webové stránky. To může, a také způsobuje výpadky webových stránek, což je činí nepřístupnými pro všechny.",
|
||||
"anubis_compromise": "Anubis je kompromis. Anubis používá schéma Proof-of-Work v duchu Hashcash, návrhu schématu proof-of-work pro snížení e-mailového spamu. Myšlenka je, že na individuálních úrovních je dodatečná zátěž zanedbatelná, ale na úrovni masového použití se sčítá a činí stahování mnohem dražším.",
|
||||
"hack_purpose": "Ve výsledku je to hack, jehož skutečným účelem je poskytnout \"dostatečně dobré\" prozatímní řešení, které nám poskytuje čas pracovat na ověřování a identifikaci robotů (např. prostřednictvím toho, jak vykreslují fonty), tak aby stránka s výzvou proof of work nemusela být prezentována uživatelům, kteří jsou legitimní.",
|
||||
"jshelter_note": "Upozorňujeme, že Anubis vyžaduje použití moderních funkcí JavaScriptu, které rozšíření jako JShelter omezují. Prosím zakažte JShelter nebo jiná podobná rozšíření pro tuto doménu.",
|
||||
"version_info": "Tato webová stránka běží na Anubis verzi",
|
||||
"try_again": "Zkusit znovu",
|
||||
"go_home": "Přejít na úvodní stránku",
|
||||
"contact_webmaster": "nebo pokud si myslíte, že byste neměli být blokováni, kontaktujte správce na",
|
||||
"connection_security": "Prosím počkejte chvilku, zatímco zajišťujeme bezpečnost vašeho připojení.",
|
||||
"javascript_required": "Bohužel musíte povolit JavaScript, abyste prošli touto výzvou. To je vyžadováno proto, že AI společnosti změnily společenskou smlouvu ohledně toho, jak funguje hosting webových stránek. Řešení bez JavaScriptu je ve vývoji.",
|
||||
"benchmark_requires_js": "Spuštění testovacího nástroje vyžaduje povolení JavaScriptu.",
|
||||
"difficulty": "Obtížnost:",
|
||||
"algorithm": "Algoritmus:",
|
||||
"compare": "Porovnat:",
|
||||
"time": "Čas",
|
||||
"iters": "Iterace",
|
||||
"time_a": "Čas A",
|
||||
"iters_a": "Iterace A",
|
||||
"time_b": "Čas B",
|
||||
"iters_b": "Iterace B",
|
||||
"static_check_endpoint": "Toto je pouze kontrolní bod pro přístup na tuto stránku.",
|
||||
"authorization_required": "Vyžadována autorizace",
|
||||
"cookies_disabled": "Váš prohlížeč je nakonfigurován tak, aby zakázal cookies. Anubis vyžaduje cookies pro legitimní zájem zajistit, že jste legitimní uživatel. Prosím povolte cookies pro tuto doménu",
|
||||
"access_denied": "Přístup zamítnut: kód chyby",
|
||||
"dronebl_entry": "DroneBL nahlásil záznam",
|
||||
"see_dronebl_lookup": "viz",
|
||||
"internal_server_error": "Interní chyba serveru: správce špatně nakonfiguroval Anubis. Kontaktujte správce a požádejte ho, aby se do systémových záznamů",
|
||||
"invalid_redirect": "Neplatné přesměrování",
|
||||
"redirect_not_parseable": "URL přesměrování nelze analyzovat",
|
||||
"redirect_domain_not_allowed": "Doména přesměrování není povolena",
|
||||
"failed_to_sign_jwt": "nepodařilo se podepsat JWT",
|
||||
"invalid_invocation": "Neplatné vyvolání MakeChallenge",
|
||||
"client_error_browser": "Chyba prohlížeče: Ujistěte se, že váš prohlížeč je aktuální a zkuste to později.",
|
||||
"oh_noes": "Ale ne!",
|
||||
"benchmarking_anubis": "Testování Anubise!",
|
||||
"you_are_not_a_bot": "Nejste robot!",
|
||||
"making_sure_not_bot": "Ujišťujeme se, že nejste robot!",
|
||||
"celphase": "CELPHASE",
|
||||
"js_web_crypto_error": "Váš prohlížeč nepodporuje funkci web.crypto. Používáte zabezpečené připojení?",
|
||||
"js_web_workers_error": "Váš prohlížeč nepodporuje web workers (Anubis je používá, aby zabránil zamrznutí vašeho prohlížeče). Máte nainstalovano rozšíření JShelter nebo podobné?",
|
||||
"js_cookies_error": "Váš prohlížeč neukládá cookies. Anubis používá cookies k určení, kteří klienti prošli výzvami uložením podepsaného tokenu v cookie. Prosím povolte ukládání cookies pro tuto doménu. Názvy cookies, které Anubis ukládá, se mohou měnit bez upozornění. Názvy a hodnoty cookies nejsou součástí veřejného API.",
|
||||
"js_context_not_secure": "Váše připojení není bezpečné!",
|
||||
"js_context_not_secure_msg": "Zkuste se připojit přes HTTPS nebo informujte správce o nastavení HTTPS. Pro více informací viz <a href=\"https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure\">MDN</a>.",
|
||||
"js_calculating": "Počítám...",
|
||||
"js_missing_feature": "Chybějící funkce",
|
||||
"js_challenge_error": "Chyba výzvy!",
|
||||
"js_challenge_error_msg": "Nepodařilo se vyřešit kontrolní algoritmus. Možná budete chtít obnovit stránku.",
|
||||
"js_calculating_difficulty": "Počítám...<br/>Obtížnost:",
|
||||
"js_speed": "Rychlost:",
|
||||
"js_verification_longer": "Ověřování trvá déle, než se očekávalo. Prosím neobnovujte stránku.",
|
||||
"js_success": "Úspěch!",
|
||||
"js_done_took": "Hotovo! Trvalo to",
|
||||
"js_iterations": "iterací",
|
||||
"js_finished_reading": "Dokončil jsem čtení, pokračovat →",
|
||||
"js_calculation_error": "Chyba výpočtu!",
|
||||
"js_calculation_error_msg": "Nepodařilo se vypočítat výzvu:"
|
||||
}
|
||||
@@ -2,20 +2,19 @@
|
||||
"loading": "Ladevorgang...",
|
||||
"why_am_i_seeing": "Warum sehe ich diese Seite?",
|
||||
"protected_by": "Geschützt durch",
|
||||
"protected_from": "From",
|
||||
"made_with": "Mit ❤️ gemacht in 🇨🇦",
|
||||
"mascot_design": "Maskottchen erstellt von",
|
||||
"ai_companies_explanation": "Diese Seite wird angezeigt, da der Betreiber der Webseite Anubis eingerichtet hat, um sie vor aggressiven KI-Website-Scrapern zu schützen. Diese können Ausfälle der Webseite verursachen, wodurch die Webseite für niemanden erreichbar ist.",
|
||||
"anubis_compromise": "Anubis stellt einen Kompromiss dar. Es verwendet eine Proof-of-Work-Methode nach Hashcash, die ursprünglich zur Bekämpfung von E-Mail-Spam entwickelt wurde. Die Idee dahinter ist, dass ein legitimer Besucher die Webseite mit einer vernachlässigbaren Verzögerung erreichen kann. Massenhaftes Scraping wird dadurch jedoch aufwändig und teuer.",
|
||||
"hack_purpose": "Man könnte dies als eine Lösung bezeichnen, die einem etwas Zeit für Fingerprinting und dem Identifizieren von Headless-Browsern verschafft. Besucher, die mit sehr hoher Wahrscheinlichkeit legitim sind, bekommen diese Seite nicht zu sehen.",
|
||||
"jshelter_note": "Anubis benötigt moderne JavaScript-Features, welche von Plugins wie JShelter deaktiviert werden. Bitte deaktiviere JShelter oder ähnliche Plugins für diese Domain.",
|
||||
"version_info": "Diese Webseite läuft mit der Anubis-Version",
|
||||
"try_again": "Erneut versuchen",
|
||||
"ai_companies_explanation": "Diese Seite wird angezeigt, da der Betreiber der Webseite Anubis eingerichtet hat, um sie vor aggressiven KI-Website-Scrapern zu schützen. Diese können Ausfälle der Webseite verursachen, wodurch die Webseite für jeden nicht erreichbar ist.",
|
||||
"anubis_compromise": "Anubis ist eine Art Kompromiss. Es verwendet die sogenannte Proof-of-Work Methode nach Hashcash, ein Mechanismus, der ursprünglich zur E-Mail-Spam-Bekämpfung entwickelt wurde. Die Idee dahinter ist, dass ein einziger User nur eine kleine Verzögerung hat, auf die Webseite zu gelangen; bei Scrapern kann das allerdings große Auswirkungen haben.",
|
||||
"hack_purpose": "Man könnte dies als eine Lösung bezeichnen, die gut genug ist, einem etwas Zeit zu verschaffen für Fingerprinting und dem Identifizieren von Headless Browsern, sodass im besten Fall normale User diese Seite garnicht erst zu sehen bekommen.",
|
||||
"jshelter_note": "Anubis benötigt moderne JavaScript-Features, welche von Plugins wie zB JShelter deaktiviert werden. Bitte deaktiviere also JShelter oder ähnliche Plugins für diese Domain.",
|
||||
"version_info": "Diese Webseite läuft mit Anubis version",
|
||||
"try_again": "Nochmal probieren",
|
||||
"go_home": "Zur Hauptseite",
|
||||
"contact_webmaster": "oder wenn Du glaubst, dass es sich hierbei um einen Fehler handelt, kontaktiere bitte den Administrator der Webseite unter",
|
||||
"connection_security": "Bitte warte einen Moment, während wir sicherstellen, dass eine sichere Verbindung verwendet wird.",
|
||||
"javascript_required": "Du musst JavaScript aktivieren, um diese Prüfung durchführen zu können. Dies ist notwendig, da KI-Unternehmen den sozialen Vertrag bezüglich des Hostings von Webseiten gebrochen haben. Eine Lösung ohne JavaScript ist in Entwicklung.",
|
||||
"benchmark_requires_js": "Für die Nutzung des Benchmark-Tools muss JavaScript aktiviert werden.",
|
||||
"contact_webmaster": "oder wenn es sich hier um einen Fehler handelt, kontaktiere bitte den Administrator der Webseite unter",
|
||||
"connection_security": "Bitte warte einen Moment während wir sicherstellen, dass eine sichere Verbindung verwendet wird.",
|
||||
"javascript_required": "Es muss leider JavaScript aktiviert werden, um den Check durchführen zu können. Dies ist leider notwendig weil Firmen im KI-Sektor die sozialen Verhältnisse geändert haben, wie Website-Hosting funktioniert. Eine Lösung ohne JavaScript ist in Entwicklung.",
|
||||
"benchmark_requires_js": "Das Benchmark-Tool benötigt das Aktivieren von JavaScript.",
|
||||
"difficulty": "Schwierigkeit:",
|
||||
"algorithm": "Algorithmus:",
|
||||
"compare": "Vergleich:",
|
||||
@@ -25,40 +24,40 @@
|
||||
"iters_a": "Iterationen A",
|
||||
"time_b": "Zeit B",
|
||||
"iters_b": "Iterationen B",
|
||||
"static_check_endpoint": "Dies ist nur ein Endpunkt, der von einem Reverse-Proxy geprüft werden kann.",
|
||||
"static_check_endpoint": "Dies ist nur ein Check-Endpunkt, der von beispielsweise einem Reverse-Proxy geprüft werden kann.",
|
||||
"authorization_required": "Zugriffserlaubnis benötigt",
|
||||
"cookies_disabled": "Cookies sind in deinem Browser deaktiviert. Anubis benötigt Cookies, um sicherzustellen, dass es sich hierbei um einen legitimen Zugriff handelt. Bitte aktiviere Cookies für diese Domain.",
|
||||
"cookies_disabled": "Cookies sind in Ihrem Browser deaktiviert. Anubis benötigt Cookies um sicherzustellen, dass es sich hierbei um einen validen Zugriff handelt. Bitte aktiviere Cookies für diese Domain.",
|
||||
"access_denied": "Zugriff verweigert: Fehlercode",
|
||||
"dronebl_entry": "Eintrag in DroneBL",
|
||||
"see_dronebl_lookup": "anzeigen",
|
||||
"internal_server_error": "Interner Server-Fehler: Der Administrator hat Anubis fehlerhaft konfiguriert. Bitte kontaktiere den Administrator und bitte ihn, die Logs zu prüfen.",
|
||||
"internal_server_error": "Interner Server Error: Misskonfiguration von Anubis. Bitte kontatkiere den Administrator damit dieser die Logs prüfen kann.",
|
||||
"invalid_redirect": "Ungültige Weiterleitung",
|
||||
"redirect_not_parseable": "URL der Weiterleitung kann nicht verarbeitet werden",
|
||||
"redirect_domain_not_allowed": "Domain der Weiterleitung nicht erlaubt",
|
||||
"failed_to_sign_jwt": "JWT konnte nicht signiert werden",
|
||||
"invalid_invocation": "Ungültiger Aufruf von MakeChallenge",
|
||||
"client_error_browser": "Client-Fehler: Bitte stelle sicher, dass dein Browser aktuell ist und versuche es später erneut.",
|
||||
"oh_noes": "Oh nein!",
|
||||
"failed_to_sign_jwt": "Signierung des JWT fehlgeschlagen",
|
||||
"invalid_invocation": "Aufrufen von MakeChallenge ungültig",
|
||||
"client_error_browser": "Client Error: Bitte stelle sicher, dass der Browser aktuell ist und probiere es später erneut.",
|
||||
"oh_noes": "Vermaledeit!",
|
||||
"benchmarking_anubis": "Benchmark wird durchgeführt!",
|
||||
"you_are_not_a_bot": "Du bist kein Bot!",
|
||||
"making_sure_not_bot": "Dein Browser wird geprüft!",
|
||||
"you_are_not_a_bot": "Sie sind kein Bot!",
|
||||
"making_sure_not_bot": "Ihr Browser wird geprüft!",
|
||||
"celphase": "CELPHASE",
|
||||
"js_web_crypto_error": "Dein Browser hat kein funktionierendes web.crypto Element. Wird eine sichere Verbindung verwendet?",
|
||||
"js_web_workers_error": "Dein Browser unterstützt keine Web-Worker (Anubis verwendet diese, damit der Browser nicht einfriert). Ist ein Plugin wie JShelter installiert?",
|
||||
"js_cookies_error": "Dein Browser speichert keine Cookies. Anubis verwendet Cookies, um nach bestandener Prüfung ein signiertes Token abzulegen. Bitte aktiviere Cookies für diese Domain. Die Cookie-Namen von Anubis könnten sich jederzeit ändern. Cookie-Namen und die gespeicherten Werte sind kein Teil der öffentlichen API.",
|
||||
"js_web_crypto_error": "Ihr Browser hat leider kein funktionierendes web.crypto Element. Wird eine sichere Verbindung verwendet?",
|
||||
"js_web_workers_error": "Ihr Browser unterstützt keine Web-Worker (Anubis verwendet diese, damit der Browser nicht unresponsive wird). Ist eventuell ein Plugin wie zB JShelter installiert?",
|
||||
"js_cookies_error": "Ihr Browser speichert keine Cookies. Anubis verwendet Cookies um ein gültiges Token zu speichern damit es wissen kann, welche Browser bereits geprüft wurden. Bitte aktiviere Cookies für diese Domain. Die Cookie-Namen von Anubis könnten sich jederzeit ändern. Cookie-Namen sind kein Teil der öffentlichen API.",
|
||||
"js_context_not_secure": "Diese Verbindung ist nicht sicher!",
|
||||
"js_context_not_secure_msg": "Bitte versuche, dich via HTTPS zu verbinden oder weise den Administrator darauf hin, HTTPS einzurichten. Mehr Informationen unter: <a href=\"https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure\">MDN</a>.",
|
||||
"js_context_not_secure_msg": "Bitte probiere, dich via HTTPS zu verbinden und lass den Webseiten-Administrator wissen, sauber HTTPS einzurichten. Mehr Informationen unter: <a href=\"https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure\">MDN</a>.",
|
||||
"js_calculating": "Berechnung wird durchgeführt...",
|
||||
"js_missing_feature": "Fehlendes Feature",
|
||||
"js_challenge_error": "Prüfung fehlgeschlagen!",
|
||||
"js_challenge_error_msg": "Der Prüf-Algorithmus konnte nicht geladen werden. Bitte lade diese Seite erneut.",
|
||||
"js_challenge_error": "Fehler während des Checks!",
|
||||
"js_challenge_error_msg": "Der Check-Algorithmus konnte nicht geladen werden. Bitte lade diese Seite erneut.",
|
||||
"js_calculating_difficulty": "Berechnung wird durchgeführt...<br/>Schwierigkeit:",
|
||||
"js_speed": "Geschwindigkeit:",
|
||||
"js_verification_longer": "Die Prüfung benötigt länger als erwartet. Bitte bleibe auf der Seite und lade diese nicht neu.",
|
||||
"js_verification_longer": "Der Check benötigt länger als erwartet. Bitte bleibe auf der Seite.",
|
||||
"js_success": "Erfolgreich!",
|
||||
"js_done_took": "Fertig! Dauer:",
|
||||
"js_iterations": "Iterationen",
|
||||
"js_finished_reading": "Fertig gelesen, weiter zur Seite →",
|
||||
"js_calculation_error": "Berechnung fehlgeschlagen!",
|
||||
"js_calculation_error_msg": "Fehler bei der Berechnung der Prüfung:"
|
||||
"js_calculation_error": "Fehler bei der Berechnung!",
|
||||
"js_calculation_error_msg": "Fehler bei der Berechnung des Checks:"
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
"loading": "Loading...",
|
||||
"why_am_i_seeing": "Why am I seeing this?",
|
||||
"protected_by": "Protected by",
|
||||
"protected_from": "From",
|
||||
"made_with": "Made with ❤️ in 🇨🇦",
|
||||
"mascot_design": "Mascot design by",
|
||||
"ai_companies_explanation": "You are seeing this because the administrator of this website has set up Anubis to protect the server against the scourge of AI companies aggressively scraping websites. This can and does cause downtime for the websites, which makes their resources inaccessible for everyone.",
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
"loading": "Cargando...",
|
||||
"why_am_i_seeing": "¿Por qué veo esto?",
|
||||
"protected_by": "Protegido por",
|
||||
"protected_from": "From",
|
||||
"made_with": "Hecho con ❤️ en 🇨🇦",
|
||||
"mascot_design": "Diseño de la mascota por",
|
||||
"ai_companies_explanation": "Ves esto porque el administrador de este sitio web ha configurado Anubis para proteger el servidor contra la plaga de empresas de IA que rastrean agresivamente los sitios web. Esto puede y causa tiempo de inactividad para los sitios web, haciendo que sus recursos sean inaccesibles para todos.",
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
{
|
||||
"loading": "Laadin...",
|
||||
"why_am_i_seeing": "Miks ma pean seda nägema?",
|
||||
"protected_by": "Kaitseb",
|
||||
"protected_from": "From",
|
||||
"made_with": "Tehtud ❤️ga 🇨🇦s",
|
||||
"mascot_design": "Maskoti disainis",
|
||||
"ai_companies_explanation": "Seda näidatakse selle pärast, et selle lehe administraator on paigaldanud Anubise, et kaitsta serverit selle nuhtluse eest, mida kujutab endast AI firmade agressiivne veebikraapimine. Selle tagajärjeks võib olla ja tihti ongi see, et veebilehed lakkavad töötamast ja keegi ei saa nendele ligi.",
|
||||
"anubis_compromise": "Anubis on kompromisslahendus. Anubis kasutab nö. töötõendi skeemi, mille sarnane oli <em>Hashcash</em>, mis oli mõeldud spämmikaitseks. Põhimõte on selles, et üksiku kasutaja tasemel on lisakoormus tajumatu, aga massiivse kraapimise tasemel see koormus läheb kõik arvesse ja muudab andmete töötluse palju kallimaks.",
|
||||
"hack_purpose": "Lõppudelõpuks on see siiski rutuga tehtud lahendus, mille tegelik eesmärk on olla \"piisavalt hea\", et oleks rohkem aega leida viise, kuidas saaks tuvastada brauseriautomaate (näiteks sellest, kuidas nad fonte joonistavad), et saavutada olukord, kus kontrollekraani ei olegi vaja näidata kasutajatele, kes on suurema tõenäosusega päriselt inimesed.",
|
||||
"jshelter_note": "NB! Anubis vajab töötamiseks kaasaegseid JavaScripti võimalusi, mida teatud pluginad nagu JShelter ära keelavad. Palun lülita JShelter või teised sellised veebilehitseja laiendused välja.",
|
||||
"version_info": "Sellel lehel jookseb Anubis, versioon",
|
||||
"try_again": "Proovi uuesti",
|
||||
"go_home": "Mine koju",
|
||||
"contact_webmaster": "või kui sa arvad, et sa ei peaks olema blokeeritud, võta ühendust veebimeistriga aadressil",
|
||||
"connection_security": "Oota korraks, me kontrollime ühenduse turvalisust.",
|
||||
"javascript_required": "Kahjuks tuleb JavaScript sisse lülitada, et sellest kontrollist mööda pääseda. See on kohustuslik, sest AI ettevõtted on muutnud ühiskondlikke norme veebimajutuse suhtes. Ilma JavaScriptita töötav versioon on alles arendamisel.",
|
||||
"benchmark_requires_js": "Kiirustesti jaoks on vajalik JavaScript sisse lülitada.",
|
||||
"difficulty": "Raskus:",
|
||||
"algorithm": "Algoritm:",
|
||||
"compare": "Võrdle:",
|
||||
"time": "Aega",
|
||||
"iters": "Korda",
|
||||
"time_a": "A aega",
|
||||
"iters_a": "A korda",
|
||||
"time_b": "B aega",
|
||||
"iters_b": "B korda",
|
||||
"static_check_endpoint": "Seda lehte vaatab ainult sinu vaheserver.",
|
||||
"authorization_required": "Ligipääs puudub",
|
||||
"cookies_disabled": "Sinu brauseris on küpsised keelatud. Anubis vajab küpsiseid töötamiseks, et aru saada, kas sa oled päris kasutaja või mitte. Palun luba küpsised sellel domeenil",
|
||||
"access_denied": "Ligipääs keelatud: veakood",
|
||||
"dronebl_entry": "DroneBL tagastas sissekande",
|
||||
"see_dronebl_lookup": "vaata",
|
||||
"internal_server_error": "Programmi sisemine viga: administraator on Anubise valesti seadistanud. Võta temaga ühendust ja palu tal otsida logidest märksõna",
|
||||
"invalid_redirect": "Vigane ümbersuunamine",
|
||||
"redirect_not_parseable": "Ümbersuunamise URL on vigane",
|
||||
"redirect_domain_not_allowed": "Ümbersuunamise domeen pole lubatud",
|
||||
"failed_to_sign_jwt": "JWT allkirjastamine ebaõnnestus",
|
||||
"invalid_invocation": "MakeChallenge väljakutsumine on vigane",
|
||||
"client_error_browser": "Kliendipoolne viga: palun kontrolli, et su brauser oleks uuendatud ja proovi uuesti.",
|
||||
"oh_noes": "Oi ei!",
|
||||
"benchmarking_anubis": "Anubise kiirustest!",
|
||||
"you_are_not_a_bot": "Sina ei ole bott!",
|
||||
"making_sure_not_bot": "Kontrollime, et sa ei ole bott!",
|
||||
"celphase": "CELPHASE",
|
||||
"js_web_crypto_error": "Sinu brauseris ei ole töötavat web.crypto elementi. Kas sa avasid selle turvakontekstis?",
|
||||
"js_web_workers_error": "Sinu brauser ei toeta veebi taustaprotsesse (Anubis kasutab neid, et su veebilehitseja ei hanguks). Kas sul on installitud mingi laiendus nagu JShelter?",
|
||||
"js_cookies_error": "Sinu brauser ei salvesta küpsiseid. Anubis kirjutab küpsise, milles on allkirjastatud sedel, et vahet teha, millised kliendid on kontrolli läbinud ja millised mitte. Palun luba küpsiste salvestamine sellel domeenil. Küpsiste nimed, mida Anubis kasutab, võivad muutuda ette teatamata. Küpsiste nimed ja väärtused ei ole avaliku liidese osa.",
|
||||
"js_context_not_secure": "Sinu brauserikontekst ei ole turvaline!",
|
||||
"js_context_not_secure_msg": "Proovi ühendada HTTPS aadressiga või anna administraatorile teada, et HTTPS on vajalik seadistada. Lisainfot vaata <a href=\"https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure\">MDNist</a>.",
|
||||
"js_calculating": "Arvutan...",
|
||||
"js_missing_feature": "Puuduv brauseri omadus",
|
||||
"js_challenge_error": "Kontrolli viga!",
|
||||
"js_challenge_error_msg": "Ei suutnud tuvastada kontrollalgoritmi. Võiksid proovida lehe uuesti laadida.",
|
||||
"js_calculating_difficulty": "Arvutan...<br/>Raskus:",
|
||||
"js_speed": "Kiirus:",
|
||||
"js_verification_longer": "Kontrollimine võtab kauem kui tavaliselt. Palun ära lae lehte uuesti.",
|
||||
"js_success": "Õnnestus!",
|
||||
"js_done_took": "Tehtud! Võttis",
|
||||
"js_iterations": "kordust",
|
||||
"js_finished_reading": "Lugesin ära, edasi →",
|
||||
"js_calculation_error": "Arvutamise viga!",
|
||||
"js_calculation_error_msg": "Ei suutnud kontrolli arvutada:"
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
{
|
||||
"loading": "Ladataan...",
|
||||
"why_am_i_seeing": "Miksi näen tämän?",
|
||||
"protected_by": "Suojan tarjoaa",
|
||||
"protected_from": "tekijänä",
|
||||
"made_with": "❤️ tehty 🇨🇦:ssa",
|
||||
"mascot_design": "Maskotin suunnitellut",
|
||||
"ai_companies_explanation": "Sivustolla on käytössä Anubis. Anubis estää robotteja lataamasta sivustoa ylettömästi. Tämä voi aiheuttaa palvelimen ylikuormituksen, joka estää ketään pääsemästä sivustolle.",
|
||||
"anubis_compromise": "Anubis on kompromissi. Anubis käyttää roskapostin vähentämiseen ehdotettua, Hashcash-järjestelmän mukaista työnnäytettä. Yksittäiselle käyttäjälle kuormitus on mitätön, mutta kasvattaa sivuston ylettömän lataamisen kuluja huomattavasti.",
|
||||
"hack_purpose": "Tämä on \"riittävän hyvä\" väliaikainen ratkaisu, joka antaa aikaa tunnistaa robotit, ettei työnnäyte sivua tarvitsisi näyttää todellisille käyttäjille.",
|
||||
"jshelter_note": "Anubis tarvitsee toimiakseen JavaScript-ominaisuuksia, jotka liitännäiset kuten jShelter estää. Otathan tällaiset liitännäiset pois käytöstä tälle verkkotunnukselle.",
|
||||
"version_info": "Sivusto käyttää Anubis versiota",
|
||||
"try_again": "Yritä uudelleen",
|
||||
"go_home": "Poistu",
|
||||
"contact_webmaster": "tai jos uskot ettei sinua tulisi estää, ota yhteyttä ylläpitäjään",
|
||||
"connection_security": "Odota hetki. Varmistamme yhteytesi tietoturvan.",
|
||||
"javascript_required": "Valitettavasti JavaScript on oltava käytössä tämän haasteen suorittamiseksi. Vaihtoehtoinen ratkaisu on työn alla.",
|
||||
"benchmark_requires_js": "JavaScript on oltava käytössä suorituskykytestin ajamiseksi.",
|
||||
"difficulty": "Vaikeus:",
|
||||
"algorithm": "Kaava:",
|
||||
"compare": "Vertailu:",
|
||||
"time": "Aika",
|
||||
"iters": "Toisto",
|
||||
"time_a": "Aika A",
|
||||
"iters_a": "Toisto A",
|
||||
"time_b": "Aika B",
|
||||
"iters_b": "Toisto B",
|
||||
"static_check_endpoint": "Tämä päätepiste on käyttämääsi käänteistä välityspalvelinta varten.",
|
||||
"authorization_required": "Valtuutus vaadittu",
|
||||
"cookies_disabled": "Selaimesi estää evästeet. Anubis tarvitsee evästeitä varmistaakseen, että olet todellinen käyttäjä. Otathan evästeet käyttöön tälle verkkotunnukselle",
|
||||
"access_denied": "Pääsy estetty: virhekoodi",
|
||||
"dronebl_entry": "DroneBL ilmoitti merkinnän",
|
||||
"see_dronebl_lookup": "katso",
|
||||
"internal_server_error": "Palvelinvirhe: Anubis on väärin määritetty. Pyydä ylläpitäjää tarkistamaan lokit",
|
||||
"invalid_redirect": "Virheellinen pyyntö",
|
||||
"redirect_not_parseable": "Uudellenohjauksen URL ei voitu jäsentää",
|
||||
"redirect_domain_not_allowed": "Uudelleenohjauksen verkkotunnus ei ole sallittu",
|
||||
"failed_to_sign_jwt": "JWT ei voitu allekirjoittaa",
|
||||
"invalid_invocation": "Virheellinen MakeChallenge-kaava",
|
||||
"client_error_browser": "Käyttäjävirhe: Varmista ettei selaimesi ole vanhentunut ja yritä uudelleen.",
|
||||
"oh_noes": "Voi ei!",
|
||||
"benchmarking_anubis": "Testataan Anubis!",
|
||||
"you_are_not_a_bot": "Et ole robotti!",
|
||||
"making_sure_not_bot": "Varmistetaan ettet ole robotti!",
|
||||
"celphase": "CELPHASE",
|
||||
"js_web_crypto_error": "Selaimesi web.crypto elementti ei toimi. Onko yhteytesi suojattu?",
|
||||
"js_web_workers_error": "Selaimesi ei tue Web Workers ominaisuutta. Anubis käyttää tätä estääkseen selaimesi lukkiutumisen. Onko sinulla liitännäinen, kuten jShelter käytössä?",
|
||||
"js_cookies_error": "Selaimesi ei tallenna evästeitä. Anubis tallentaa allekirjoitetun merkinnän evästeeseen, tunnistaakseen haasteen läpäisseet käyttäjät. Sallithan evästeiden tallentamisen tälle verkkotunnukselle. Tallennettujen evästeiden nimet voivat vaihdella. Evästeiden nimet ja arvot eivät ole osa julkista rajapintaa.",
|
||||
"js_context_not_secure": "Yhteytesi ei ole suojattu!",
|
||||
"js_context_not_secure_msg": "Yhdistä käyttäen HTTPS tai pyydä ylläpitäjää määrittämään HTTPS. Saadaksesi lisätietoja, katso <a href=\"https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure\">MDN</a>.",
|
||||
"js_calculating": "Lasketaan...",
|
||||
"js_missing_feature": "Puuttuva ominaisuus",
|
||||
"js_challenge_error": "Haastevirhe!",
|
||||
"js_challenge_error_msg": "Tarkistuskaavaa ei voitu ratkaista. Voit yrittää ladata sivua uudelleen.",
|
||||
"js_calculating_difficulty": "Lasketaan...<br/>Vaikeus:",
|
||||
"js_speed": "Nopeus:",
|
||||
"js_verification_longer": "Vahvistus kestää odotettua pitempään. Ethän lataa sivua uudelleen.",
|
||||
"js_success": "Onnistui!",
|
||||
"js_done_took": "Valmis! Kesti",
|
||||
"js_iterations": "toistot",
|
||||
"js_finished_reading": "Luettu, jatka →",
|
||||
"js_calculation_error": "Laskentavirhe!",
|
||||
"js_calculation_error_msg": "Haasteen laskenta ei onnistunut:"
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
{
|
||||
"loading": "Naglo-load...",
|
||||
"why_am_i_seeing": "Bakit nakikita ko ito?",
|
||||
"protected_by": "Pinoprotekta ng",
|
||||
"protected_from": "mula sa",
|
||||
"made_with": "Ginawa na may ❤️ sa 🇨🇦",
|
||||
"mascot_design": "Disenyo ng Maskot ni/ng",
|
||||
"ai_companies_explanation": "Nakikita mo ito dahil ang tagapangasiwa ng website na ito ay nag-set up ng Anubis upang protektahan ang server laban sa salot ng mga kumpanya ng AI na aggresibong nagse-scrape ng mga website. Maaari nitong magdulot ng downtime para sa mga website, na gagawing hindi naa-access ang kanilang mga resource para sa lahat.",
|
||||
"anubis_compromise": "Isang kompromiso ang Anubis. Gumagamit ang Anubis ng isang Proof-of-Work na scheme sa ugat ng Hashcash, isang iminungkahing proof-of-work scheme upang mabawasan ang email spam. Ang ideya ay sa indibidwal na scale hindi napapansin ang karagdagang load, ngunit sa malaking antas ng pag-scrape nagkararagdag ito at ginagawang mas mahal ang pag-scrape.",
|
||||
"hack_purpose": "Sa huli, ito ay isang hack na ang totoong layunin ay magbigay ng \"sapat na mabuti\" na placeholder na solusyon upang mas maraming oras ang magugugol sa pag-fingerprint at pagtukoy ng mga walang ulo na browser (hal: sa pamamagitan ng kung paano nila ginagawa ang pag-render ng font) nang sa gayon ay hindi na kailangang iharap sa mga user na mas malamang na maging lehitimo ang patunay ng hamon sa pahina ng trabaho.",
|
||||
"jshelter_note": "Pakitandaan na kinakailangan ng Anubis ang paggamit ng modernong JavaScript na feature na idi-disable ng mga plugin tulad ng JShelter. Mangyaring i-disable ang JShelter o ibang mga plugin para sa domain na ito.",
|
||||
"version_info": "Ang website na ito ay tumatakbo ng Anubis bersyon",
|
||||
"try_again": "Subukan muli",
|
||||
"go_home": "Bumalik sa panimula",
|
||||
"contact_webmaster": "o kung naniniwala ka na hindi ka dapat na-block, mangyaring makipag-ugnayan sa mga webmaster sa",
|
||||
"connection_security": "Mangyaring maghintay nang ilang sandali habang sinisigurado namin ang seguridad ng iyong koneksyon.",
|
||||
"javascript_required": "Nakalulungkot, ngunit kailangan mong paganahin ang JavaScript upang malampasan ang hamong ito. Ito ay kinakailangan dahil binago ng mga kumpanya ng AI ang social contract tungkol sa kung paano gumagana ang pagho-host ng website. Ang isang walang-JS na solusyon ay isang work-in-progress.",
|
||||
"benchmark_requires_js": "Kinakailangang naka-enable ang JavaScript upang patakbuhin ang benchmark tool.",
|
||||
"difficulty": "Kahirapan:",
|
||||
"algorithm": "Algoritmo:",
|
||||
"compare": "Kumpara:",
|
||||
"time": "Oras",
|
||||
"iters": "Mga Iterasyon",
|
||||
"time_a": "Time A",
|
||||
"iters_a": "Iters A",
|
||||
"time_b": "Time B",
|
||||
"iters_b": "Iters B",
|
||||
"static_check_endpoint": "Isa lang itong check endpoint para magamit ng iyong reverse proxy.",
|
||||
"authorization_required": "Kinakailangan ang pagpapatunay",
|
||||
"cookies_disabled": "Ang iyong browser ay na-configure upang hindi paganahin ang cookies. Kinakailangan ng Anubis ang cookies para sa lehitimong interes ng pagtiyak na ikaw ay isang wastong kliyente. Mangyaring paganahin ang cookies para sa domain na ito",
|
||||
"access_denied": "Tinanggihan ang Access: error code",
|
||||
"dronebl_entry": "Nag-ulat ang DroneBL ng entry",
|
||||
"see_dronebl_lookup": "tignan ang",
|
||||
"internal_server_error": "Internal Server Error: hindi na-configure nang mabuti ng tagapangasiwa ang Anubis. Makipag-ugnayan sa tagapangasiwa at sabihin sa kanila na tumingin sa mga log sa paligid ng",
|
||||
"invalid_redirect": "Hindi wastong redirect",
|
||||
"redirect_not_parseable": "Hindi ma-parse ang redirect URL",
|
||||
"redirect_domain_not_allowed": "Hindi pinapayagan ang redirect domain",
|
||||
"failed_to_sign_jwt": "nabigong ilagda ang JWT",
|
||||
"invalid_invocation": "Hindi wastong panawagan para sa MakeChallenge",
|
||||
"client_error_browser": "Error sa Kliyente: Pakitiyak na napapanahon ang iyong browser at subukang muli sa ibang pagkakataon.",
|
||||
"oh_noes": "Ay, naku!",
|
||||
"benchmarking_anubis": "Binebenchmark ang Anubis!",
|
||||
"you_are_not_a_bot": "Hindi ka isang bot!",
|
||||
"making_sure_not_bot": "Sinisigurado na hindi ka isang bot!",
|
||||
"celphase": "CELPHASE",
|
||||
"js_web_crypto_error": "Ang iyong browser ay walang gumaganang web.crypto element. Tinitingnan mo ba ito sa isang secure na konteksto?",
|
||||
"js_web_workers_error": "Hindi sinusuportahan ng iyong browser ang mga web worker (ginagamit ito ng Anubis upang maiwasan ang pag-freeze ng iyong browser). Mayroon ka bang naka-install na plugin tulad ng JShelter?",
|
||||
"js_cookies_error": "Ang iyong browser ay hindi nag-iimbak ng cookies. Gumagamit ang Anubis ng cookies upang matukoy kung aling mga kliyente ang nakapasa sa mga hamon sa pamamagitan ng pag-iimbak ng isang nilagdaang token sa isang cookie. Mangyaring paganahin ang pag-iimbak ng cookies para sa domain na ito. Ang mga pangalan ng cookies na iniimbak ng Anubis ay maaaring mag-iba nang walang abiso. Ang mga pangalan at value ng cookie ay hindi bahagi ng pampublikong API.",
|
||||
"js_context_not_secure": "Hindi secure ang iyong konteksto!",
|
||||
"js_context_not_secure_msg": "Subukang kumonekta sa pamamagitan ng HTTPS o sabihin sa admin na i-set up ang HTTPS. Para sa karagdagang impormasyon, tignan ang <a href=\"https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure\">MDN</a>.",
|
||||
"js_calculating": "Kinakalkula...",
|
||||
"js_missing_feature": "Nawawalang feature",
|
||||
"js_challenge_error": "Error sa hamon!",
|
||||
"js_challenge_error_msg": "Nabigong iresolba ang algoritmo ng pagsusuri. Baka gusto mong i-reload ang pahina.",
|
||||
"js_calculating_difficulty": "Kinakalkula...<br/>Kahirapan:",
|
||||
"js_speed": "Bilis:",
|
||||
"js_verification_longer": "Mas tumatagal ang pag-verify kaysa sa inaasahan. Mangyaring huwag i-refresh ang pahina.",
|
||||
"js_success": "Matagumpay!",
|
||||
"js_done_took": "Tapos na! Nagtagal nang",
|
||||
"js_iterations": "mga iterasyon",
|
||||
"js_finished_reading": "Tapos na akong magbasa, magpatuloy →",
|
||||
"js_calculation_error": "Error sa pagkalkula!",
|
||||
"js_calculation_error_msg": "Nabigong ikalkula ang hamon:"
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
"loading": "Chargement...",
|
||||
"why_am_i_seeing": "Pourquoi je vois ceci ?",
|
||||
"protected_by": "Protégé par",
|
||||
"protected_from": "From",
|
||||
"made_with": "Fait avec ❤️ au 🇨🇦",
|
||||
"mascot_design": "Design de la mascotte par",
|
||||
"ai_companies_explanation": "Vous voyez ceci car l'administrateur de ce site web a configuré Anubis pour protéger le serveur contre le fléau des entreprises d'IA qui scrapent agressivement les sites web. Cela peut et cause des temps d'arrêt pour les sites web, ce qui rend leurs ressources inaccessibles pour tout le monde.",
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
{
|
||||
"loading": "Hleður...",
|
||||
"why_am_i_seeing": "Af hverju er ég að sjá þetta?",
|
||||
"protected_by": "Verndað með",
|
||||
"protected_from": "From",
|
||||
"made_with": "Gert í 🇨🇦 með ❤️",
|
||||
"mascot_design": "Lukkudýrið hannað af",
|
||||
"ai_companies_explanation": "Þú ert að sjá þetta vegna þess að kerfisstjóri þessa vefsvæðis hefur sett upp Anubis til að vernda vefþjóninn fyrir holskeflu beiðna frá svokölluðum gervigreindarfyrirtækjum sem samviskulaust eru að skrapa upplýsingar af vefsvæðum annarra. Þetta getur valdið og veldur töfum og truflunum á þessum vefsvæðum, sem aftur veldur því að efni þeirra verður öllum óaðgengilegt.",
|
||||
"anubis_compromise": "Anubis er millivegur. Anubis notar sönnun-á-vinnu (Proof-of-Work) skema í líkingu við Hashcash, sem er viðlíka skema til að minnka ruslpóst. Hugmyndin er að fyrir almennar heimsóknir verði viðbótarálagið vegna þessa ásættanlegt og valdi litlum truflunum, en við massaskröpun verði samlegðaráhrifin veruleg og geri slíka skröpun upplýsinga of dýra hvað varðar afköst og reiknigetu.",
|
||||
"hack_purpose": "Hafa verður í huga að þetta er \"redding\" sem hefur þann tilgang að gefa \"nógu góða\" bráðabirgðalausn við þessu vandamáli, þannig að hægt sé á meðan að finna betri aðferðir við að auðkenna og gera fingraför til að þekkja viðmótslausa vafra (headless browsers - til dæmis með því hvernig þeir meðhöndla letur) þannig að í framtíðinni þurfi ekki að birta síðuna með sönnun-á-vinnu áskoruninni fyrir notendur sem líklegir eru til að vera löglegir/mennskir notendur.",
|
||||
"jshelter_note": "Athugaðu að Anubis krefst notkunar á ýmsum nútímalegum eiginleikum JavaScript sem viðbætur á borð við JShelter munu gera ávirka. Endilega gerðu JShelter eða álíka viðbætur óvirkar fyrir þetta lén.",
|
||||
"version_info": "Þetta vefsvæði er að keyra Anubis útgáfu",
|
||||
"try_again": "Prófaðu aftur",
|
||||
"go_home": "Farðu aftur heim til þín",
|
||||
"contact_webmaster": "eða ef þú heldur að ekki ætti að loka á þig, þá ættirðu að hafa samband við vefstjórann á",
|
||||
"connection_security": "Hinkraðu augnablik á meðan við tryggjum öryggi tengingarinnar þinnar.",
|
||||
"javascript_required": "Það er leiðinlegt, en þú verður að virkja JavaScript til að komast í gegnum þessa áskorun. Þetta er nauðsynlegt vegna þess að AI-fyrirtækin neita að fara eftir þeim samfélagslegu viðmiðum sem hafa mótað það hvernig vefhýsing virkar. Lausn sem ekki reiðir sig á JS er í vinnslu.",
|
||||
"benchmark_requires_js": "JavaScript þarf að vera virkt til að keyra afkastaprófunarkerfið.",
|
||||
"difficulty": "Erfiðleikastig:",
|
||||
"algorithm": "Reiknirit:",
|
||||
"compare": "Bera saman:",
|
||||
"time": "Tími",
|
||||
"iters": "Umferðir",
|
||||
"time_a": "Tími A",
|
||||
"iters_a": "Umferðir A",
|
||||
"time_b": "Tími B",
|
||||
"iters_b": "Umferðir B",
|
||||
"static_check_endpoint": "Þetta er bara endapunktur prófunar til notkunar fyrir öfuga milliþjóninn (reverse proxy) þinn.",
|
||||
"authorization_required": "Auðkenning nauðsynleg",
|
||||
"cookies_disabled": "Vafrinn þinn er stilltur á að gera vefkökur óvirkar. Anubis þarf að nota vefkökur í þeim tilgangi að tryggja að þú sért með leyfilegt forrit. Vinsamlega virkjaðu vefkökur fyrir þetta lén",
|
||||
"access_denied": "Aðgangi hafnað: villukóði",
|
||||
"dronebl_entry": "DroneBL tilkynnti færslu",
|
||||
"see_dronebl_lookup": "skoðaðu",
|
||||
"internal_server_error": "Innri villa á netþjóni: Kerfisstjóri hefur stillt Anubis rangt. Hafðu samband við kerfisstjóra og biddu þá um að skoða atvikaskrár sem tengjast þessu",
|
||||
"invalid_redirect": "Ógild endurbeining",
|
||||
"redirect_not_parseable": "Slóð endurbeiningar er ekki túlkanleg",
|
||||
"redirect_domain_not_allowed": "Lén endurbeiningar er ekki leyft",
|
||||
"failed_to_sign_jwt": "mistókst að undirrita JWT",
|
||||
"invalid_invocation": "Ógild kvaðning á MakeChallenge",
|
||||
"client_error_browser": "Villa í forriti: Gakktu úr skugga um að vafrinn þinn sé uppfærður í nýjustu útgáfu og prófaðu aftur síðar.",
|
||||
"oh_noes": "Æi nei!",
|
||||
"benchmarking_anubis": "Afkastaprófun Anubis!",
|
||||
"you_are_not_a_bot": "Þú ert ekki botti!",
|
||||
"making_sure_not_bot": "Geng úr skugga um að þú sért ekki botti!",
|
||||
"celphase": "CELPHASE",
|
||||
"js_web_crypto_error": "Vafrinn þinn er ekki með web.crypto einindi sem virkar. Ertu að skoða þetta í gegnum öruggt umhverfi?",
|
||||
"js_web_workers_error": "Vafrinn þinn styður ekki vefvaktara (web workers - Anubis notar þetta til að koma í veg fyrir að vafrinn frjósi). Ertu með viðbót á borð við JShelter uppsetta?",
|
||||
"js_cookies_error": "Vafrinn þinn geymir ekki vefkökur. Anubis notar vefkökur til að ákvarða hvaða biðlaraforrit hafi leyst áskoranir og geymir þá undirritað teikn í vefköku. Vinsamlega virkjaðu geymslu á vefkökum fyrir þetta lén. Nöfnin á þeim vefkökum sem Anubis geymir geta breyst fyrirvaralaust. Heiti vefkakna og gildi þeirra eru ekki hluti opinbera API-kerfisviðmótsins.",
|
||||
"js_context_not_secure": "Umhverfið þitt er ekki öruggt!",
|
||||
"js_context_not_secure_msg": "Prófaðu að tengjast í gegnum HTTPS eða láttu kerfisstjórann vita að hann þurfi að setja upp HTTPS. Fyrir nánari upplýsingar er hægt að skoða <a href=\"https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure\">MDN</a>.",
|
||||
"js_calculating": "Reikna...",
|
||||
"js_missing_feature": "Eiginleika vantar",
|
||||
"js_challenge_error": "Villa í áskorun!",
|
||||
"js_challenge_error_msg": "Mistókst að leysa reiknirit á prófunar. Þú gætir viljað endurlesa síðuna.",
|
||||
"js_calculating_difficulty": "Reikna...<br/>Erfiðleikastig:",
|
||||
"js_speed": "Hraði:",
|
||||
"js_verification_longer": "Sannvottun tók lengri tíma en búast má við. Ekki endurlesa síðuna.",
|
||||
"js_success": "Tókst!",
|
||||
"js_done_took": "Klárt! Tók",
|
||||
"js_iterations": "umferðir",
|
||||
"js_finished_reading": "Ég hef lokið lestrinum, höldum áfram →",
|
||||
"js_calculation_error": "Reiknivilla!",
|
||||
"js_calculation_error_msg": "Mistókst að reikna áskorun:"
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
{
|
||||
"loading": "Caricamento...",
|
||||
"why_am_i_seeing": "Perché vedo questa schermata?",
|
||||
"protected_by": "Protetto da",
|
||||
"protected_from": "From",
|
||||
"made_with": "Realizzato con ❤️ in 🇨🇦",
|
||||
"mascot_design": "Mascotte disegnata da",
|
||||
"ai_companies_explanation": "Vedi questa schermata perché l'amministratore di questo sito web ha installato Anubis per proteggere il server dalla piaga delle aziende di AI generativa che estraggono, senza freno, dati dai siti web. Questo comportamento causa disservizi per i siti web, rendendoli inaccessibili a tutti.",
|
||||
"anubis_compromise": "Anubis è un compromesso. Anubis utilizza un meccanismo di proof-of-work in stile Hashcash, un meccanismo per ridurre le email di spam. L'idea è che, a livello individuale, il lavoro aggiuntivo necessario per la proof-of-work sia trascurabile, ma, a livello di grandi reti di bot, il lavoro si somma e diventa molto più costoso.",
|
||||
"hack_purpose": "In sostanza, questo è un espediente il cui scopo è fornire una soluzione temporanea e ragionevolmente funzionante, nel mentre che vengono ricercate soluzioni migliori nel riconoscimento e nell'identificazione di programmi automatici per la navigazione web (per esempio: osservando il modo in cui disegnano i caratteri), così che la pagina della proof-of-work non debba essere sottoposta agli utenti che hanno maggiore probabilità di essere legittimi.",
|
||||
"jshelter_note": "Si noti che Anubis richiede l'utilizzo di caratteristiche moderne di JavaScript che alcuni plugin, come JShelter, disabilitano. Per accedere, disabilita JShelter (o altri plugin simili) per questo dominio.",
|
||||
"version_info": "Questo sito sta usando Anubis versione",
|
||||
"try_again": "Riprova",
|
||||
"go_home": "Vai alla home",
|
||||
"contact_webmaster": "o, se pensi di non dover essere bloccato, contatta l'amministratore a",
|
||||
"connection_security": "Un momento: stiamo controllando la sicurezza della tua connessione.",
|
||||
"javascript_required": "Purtroppo, devi abilitare Javascript per riuscire a superare questa pagina. Questa misura è necessaria perché alcune compagnie di AI hanno unilateralmente deciso di violare il contratto sociale sulla fornitura di siti web. Stiamo lavorando ad una soluzione che non richieda Javascript.",
|
||||
"benchmark_requires_js": "Per eseguire lo strumento di test, è necessario abilitare Javascript.",
|
||||
"difficulty": "Difficoltà:",
|
||||
"algorithm": "Algoritmo:",
|
||||
"compare": "Test:",
|
||||
"time": "Tempo",
|
||||
"iters": "Iterazioni",
|
||||
"time_a": "Tempo A",
|
||||
"iters_a": "Iterazioni A",
|
||||
"time_b": "Tempo B",
|
||||
"iters_b": "Iterazioni B",
|
||||
"static_check_endpoint": "Questo è solo un endpoint di test da utilizzare col reverse proxy.",
|
||||
"authorization_required": "Autorizzazione necessaria",
|
||||
"cookies_disabled": "Il tuo browser è configurato per disabilitare i cookies. Anubis richiede i cookie per accertarsi che tu sia un visitatore umano, ed è un legittimo interesse. Per favore, abilita i cookie per questo dominio.",
|
||||
"access_denied": "Accesso negato: errore",
|
||||
"dronebl_entry": "DroneBL ha riportato un record",
|
||||
"see_dronebl_lookup": "vedi",
|
||||
"internal_server_error": "Internal Server Error: Anubis non è configurato correttamente. Contattare l'amministratore e chiedergli di controllare i log attorno a",
|
||||
"invalid_redirect": "Reindirizzamento non valido",
|
||||
"redirect_not_parseable": "Errore di sintassi nel reindirizzamento",
|
||||
"redirect_domain_not_allowed": "Dominio non permesso per il reindirizzamento",
|
||||
"failed_to_sign_jwt": "Impossibile firmare JWT",
|
||||
"invalid_invocation": "Chiamata non valida a MakeChallenge",
|
||||
"client_error_browser": "Client Error: assicurati che il tuo browser sia aggiornato e riprova.",
|
||||
"oh_noes": "Oh no!",
|
||||
"benchmarking_anubis": "Testando Anubis!",
|
||||
"you_are_not_a_bot": "Non sei un robot!",
|
||||
"making_sure_not_bot": "Controllo se sei un robot...",
|
||||
"celphase": "CELPHASE",
|
||||
"js_web_crypto_error": "Il tuo browser non ha un elemento web.crypto funzionante. Stai utilizzando una connessione sicura?",
|
||||
"js_web_workers_error": "Il tuo browser non supporta web workers (Anubis li utilizza per evitare di rallentare il tuo browser). Hai installato un plugin come JShelter?",
|
||||
"js_cookies_error": "Il tuo browser non salva i cookie. Anubis utilizza i cookie per determinare quali client hanno superato la prova, salvando un identificativo firmato digitalmente in un cookie. Abilita il salvataggio dei cookie per questo dominio. Il nome del cookie salvato da Anubis potrebbe cambiare senza preavviso. I nomi dei cookie e il loro contenuto non fanno parte dell'API pubblica.",
|
||||
"js_context_not_secure": "La tua connessione non è sicura!",
|
||||
"js_context_not_secure_msg": "Prova a connetterti tramite HTTPS, o fallo abilitare dall'amministratore del sito. Per maggiori informazioni, vedi <a href=\"https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure\">MDN</a>.",
|
||||
"js_calculating": "Calcolo in corso...",
|
||||
"js_missing_feature": "Funzionalità mancante",
|
||||
"js_challenge_error": "Errore nel test!",
|
||||
"js_challenge_error_msg": "Impossibile trovare l'algoritmo di controllo. Ricarica la pagina.",
|
||||
"js_calculating_difficulty": "Calcolo in corso...<br/>Difficoltà:",
|
||||
"js_speed": "Velocità:",
|
||||
"js_verification_longer": "La verifica sta richiedendo più tempo del previsto. Non aggiornare la pagina: attendere.",
|
||||
"js_success": "Successo!",
|
||||
"js_done_took": "Fatto! Sono state necessarie",
|
||||
"js_iterations": "iterazioni.",
|
||||
"js_finished_reading": "Ho finito di leggere, continua →",
|
||||
"js_calculation_error": "Errore nel calcolo!",
|
||||
"js_calculation_error_msg": "Impossibile superare il test:"
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
{
|
||||
"loading": "ロード中...",
|
||||
"why_am_i_seeing": "なぜこれが表示されるのですか?",
|
||||
"protected_by": "Protected by",
|
||||
"protected_from": "From",
|
||||
"made_with": "Made with ❤️ 🇨🇦",
|
||||
"mascot_design": "Mascot design by",
|
||||
"ai_companies_explanation": "このメッセージが表示されているのは、このウェブサイトの管理者が、AI企業による過剰なウェブスクレイピングからサーバーを守るためにAnubisを導入しているためです。これにより、ウェブサイトがダウンし、すべての利用者がリソースにアクセスできなくなる事態が発生することがあります。",
|
||||
"anubis_compromise": "Anubisは妥協策です。AnubisはHashcashのようなProof-of-Work方式を採用しており、これは元々メールスパムを減らすために提案された仕組みです。個人レベルでは追加の負荷は無視できる程度ですが、大規模なスクレイピングでは負荷が積み重なり、スクレイピングのコストが大幅に増加します。",
|
||||
"hack_purpose": "最終的には、これは「十分に使える」仮の解決策を提供するためのハックです。本来の目的は、フォントレンダリングの方法などを通じてヘッドレスブラウザの指紋付けや識別により多くの時間を割けるようにし、正当なユーザーに対してはProof-of-Workのチャレンジページを表示しなくて済むようにすることです。",
|
||||
"jshelter_note": "Anubisは、JShelterのようなプラグインが無効化する最新のJavaScript機能を必要とします。このドメインではJShelterや同様のプラグインを無効にしてください。",
|
||||
"version_info": "このウェブサイトはAnubisバージョンで動作しています",
|
||||
"try_again": "再試行",
|
||||
"go_home": "ホームに戻る",
|
||||
"contact_webmaster": "もしブロックされるべきでないと思われる場合は、ウェブマスターにご連絡ください:",
|
||||
"connection_security": "接続の安全性を確認しています。しばらくお待ちください。",
|
||||
"javascript_required": "申し訳ありませんが、このチャレンジを通過するにはJavaScriptを有効にする必要があります。これはAI企業がウェブホスティングの社会的契約を変えてしまったためです。JavaScriptなしの解決策は現在開発中です。",
|
||||
"benchmark_requires_js": "ベンチマークツールを実行するにはJavaScriptを有効にする必要があります。",
|
||||
"difficulty": "難易度:",
|
||||
"algorithm": "アルゴリズム:",
|
||||
"compare": "比較:",
|
||||
"time": "時間",
|
||||
"iters": "イテレーション数",
|
||||
"time_a": "時間A",
|
||||
"iters_a": "イテレーションA",
|
||||
"time_b": "時間B",
|
||||
"iters_b": "イテレーションB",
|
||||
"static_check_endpoint": "これはリバースプロキシ用のチェックエンドポイントです。",
|
||||
"authorization_required": "認証が必要です",
|
||||
"cookies_disabled": "お使いのブラウザはCookieを無効にしています。Anubisは、あなたが正当なクライアントであることを確認するためにCookieを必要とします。このドメインでCookieを有効にしてください。",
|
||||
"access_denied": "アクセス拒否: エラーコード",
|
||||
"dronebl_entry": "DroneBLにエントリーが報告されました",
|
||||
"see_dronebl_lookup": "参照",
|
||||
"internal_server_error": "内部サーバーエラー: 管理者がAnubisの設定を誤っています。管理者に連絡し、次のログを確認するよう依頼してください:",
|
||||
"invalid_redirect": "無効なリダイレクト",
|
||||
"redirect_not_parseable": "リダイレクトURLを解析できません",
|
||||
"redirect_domain_not_allowed": "リダイレクトドメインは許可されていません",
|
||||
"failed_to_sign_jwt": "JWTの署名に失敗しました",
|
||||
"invalid_invocation": "MakeChallengeの無効な呼び出し",
|
||||
"client_error_browser": "クライアントエラー: ブラウザが最新であることを確認し、後でもう一度お試しください。",
|
||||
"oh_noes": "Oh noes!",
|
||||
"benchmarking_anubis": "Anubisのベンチマーク中!",
|
||||
"you_are_not_a_bot": "あなたはボットではありません!",
|
||||
"making_sure_not_bot": "あなたがボットでないことを確認しています!",
|
||||
"celphase": "CELPHASE",
|
||||
"js_web_crypto_error": "お使いのブラウザには正常に動作するweb.crypto要素がありません。安全なコンテキストで閲覧していますか?",
|
||||
"js_web_workers_error": "お使いのブラウザはWebワーカーをサポートしていません(Anubisはこれでブラウザのフリーズを防ぎます)。JShelterのようなプラグインを使用していませんか?",
|
||||
"js_cookies_error": "お使いのブラウザはCookieを保存しません。Anubisは、チャレンジを通過したクライアントを判別するために署名付きトークンをCookieに保存します。このドメインでCookieの保存を有効にしてください。Anubisが保存するCookie名は予告なく変更される場合があります。Cookie名や値は公開APIの一部ではありません。",
|
||||
"js_context_not_secure": "お使いのコンテキストは安全ではありません!",
|
||||
"js_context_not_secure_msg": "HTTPSで接続するか、管理者にHTTPSの設定を依頼してください。詳細は<a href=\"https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure\">MDN</a>をご覧ください。",
|
||||
"js_calculating": "計算中...",
|
||||
"js_missing_feature": "機能がありません",
|
||||
"js_challenge_error": "チャレンジエラー!",
|
||||
"js_challenge_error_msg": "チェックアルゴリズムの解決に失敗しました。ページを再読み込みしてください。",
|
||||
"js_calculating_difficulty": "計算中...<br/>難易度:",
|
||||
"js_speed": "速度:",
|
||||
"js_verification_longer": "検証に予想以上の時間がかかっています。ページをリフレッシュしないでください。",
|
||||
"js_success": "成功!",
|
||||
"js_done_took": "完了!所要時間",
|
||||
"js_iterations": "イテレーション数",
|
||||
"js_finished_reading": "読み終わりました。続行 →",
|
||||
"js_calculation_error": "計算エラー!",
|
||||
"js_calculation_error_msg": "チャレンジの計算に失敗しました:"
|
||||
}
|
||||
@@ -1,22 +1,3 @@
|
||||
{
|
||||
"supportedLanguages": [
|
||||
"cs",
|
||||
"de",
|
||||
"en",
|
||||
"es",
|
||||
"et",
|
||||
"fi",
|
||||
"fil",
|
||||
"fr",
|
||||
"is",
|
||||
"it",
|
||||
"ja",
|
||||
"nb",
|
||||
"nn",
|
||||
"pt-BR",
|
||||
"ru",
|
||||
"tr",
|
||||
"zh-CN",
|
||||
"zh-TW"
|
||||
]
|
||||
}
|
||||
"supportedLanguages": ["en", "fr", "es", "pt-BR", "de", "tr"]
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
{
|
||||
"loading": "Laster inn...",
|
||||
"why_am_i_seeing": "Hvorfor ser jeg dette?",
|
||||
"protected_by": "Beskyttet av",
|
||||
"protected_from": "Fra",
|
||||
"made_with": "Laget med ❤️ i 🇨🇦",
|
||||
"mascot_design": "Maskotdesign av",
|
||||
"ai_companies_explanation": "Du ser dette fordi administratoren av dette nettstedet har satt opp Anubis for å beskytte sørveren mot plagen av KI-selskaper som aggressivt skraper nettsteder. Dette kan, og fortsetter med å, forårsake driftstans for nettstedene, som gjør ressursene deres utilgjengelige for alle.",
|
||||
"anubis_compromise": "Anubis er et kompromiss. Anubis bruker et «Proof-of-Work»-skjema som ligner på Hashcash, et lignende skjema for å redusere søppel-e-post. Idéen er at ved småstilte tilfeller er den ytterligere belastningen ignorerbar, men ved storstilt skraping samler den på seg fart og gjør det å skrape mye mer dyrt.",
|
||||
"hack_purpose": "Til syvende og sist er dette en hack som har som formål å gi en «god nok» plassholderløsning slik at mer tid kan brukes på å fingeravtrykke og gjenkjenne hodeløse nettlesere (f.eks. hvordan de gjengir skrifttyper) slik at utfordringssiden ikke må bli vist til brukere som er mer sannsynligvis ekte.",
|
||||
"jshelter_note": "NB: Anubis krever bruk av moderne JavaScript-funksjoner som tillegg som JShelter slår av. Vennligst slå av JShelter eller lignende tillegg for dette domenet.",
|
||||
"version_info": "Dette nettstedet kjører Anubis-utgave",
|
||||
"try_again": "Prøv igjen",
|
||||
"go_home": "Gå hjem",
|
||||
"contact_webmaster": "eller om du synes at du ikke burde være blokkert, vennligst ta kontakt med administratoren på",
|
||||
"connection_security": "Vennligst vent mens vi bekrefter tryggheten av tilkoblingen din.",
|
||||
"javascript_required": "Du må dessverre slå på JavaScript for å komme deg forbi denne utfordringen. Dette kreves fordi KI-selskaper har endret sosialkontrakten om hvordan nettstedsverting fungerer. En ikke-JS-løsning er i gang med å skapes.",
|
||||
"benchmark_requires_js": "JavaScript må være påslått for å kjøre sammenligningsverktøyet.",
|
||||
"difficulty": "Vanskelighetsnivå:",
|
||||
"algorithm": "Algoritme:",
|
||||
"compare": "Jevnfør:",
|
||||
"time": "Tid",
|
||||
"iters": "Gjentakelser",
|
||||
"time_a": "Tid A",
|
||||
"iters_a": "Gjentakelser A",
|
||||
"time_b": "Tid B",
|
||||
"iters_b": "Gjentakelser B",
|
||||
"static_check_endpoint": "Dette er bare et sjekkeendepunkt for din omvendte proxy å bruke.",
|
||||
"authorization_required": "Legitimasjon kreves",
|
||||
"cookies_disabled": "Nettleseren din er konfigurert for å avslå informasjonskapsler. Anubis krever informasjonskapsler for å bekrefte at du er en ekte bruker. Vennligst slå på informasjonskapsler på dette domenet.",
|
||||
"access_denied": "Adgang nektet: feilkode",
|
||||
"dronebl_entry": "DroneBL rapporterte em oppføring.",
|
||||
"see_dronebl_lookup": "se",
|
||||
"internal_server_error": "Intern serverfeil: administratoren har feilkonfigurert Anubis. Vennligst ta kontakt med hen og spør hen om å se gjennom loggene om",
|
||||
"invalid_redirect": "Ugyldig omdirigering",
|
||||
"redirect_not_parseable": "Omdirigerings-URL-en kunne ikkj tolkes",
|
||||
"redirect_domain_not_allowed": "Omdirigeringsdomenet er ikke tillatt",
|
||||
"failed_to_sign_jwt": "mislyktes i å signere JWT",
|
||||
"invalid_invocation": "Ugyldig fremkalling av MakeChallenge",
|
||||
"client_error_browser": "Klientfeil: Vennligst sørg for at at nettleseren din er oppdatert og prøv igjen senere.",
|
||||
"oh_noes": "Å nei!",
|
||||
"benchmarking_anubis": "Sammenligner Anubis!",
|
||||
"you_are_not_a_bot": "Du er ikke en bot!",
|
||||
"making_sure_not_bot": "Bekrefter at du ikke er en bot!",
|
||||
"celphase": "CELPHASE",
|
||||
"js_web_crypto_error": "Nettleseren din har ikke et fungerande web.crypto-element. Ser du dette med ei sikker tilkopling?",
|
||||
"js_web_workers_error": "Nettleseren din støtter ikke nettarbeidere (Anubis bruker dette for å unngå å fryse nettleseren din). Har du et tillegg som JShelter installert?",
|
||||
"js_cookies_error": "Nettleseren lagrer ikke informasjonskapsler. Anubis bruker informasjonskapsler for å avgjøre hvilke klienter har lyktes i utfordringen ved å lagre en signert token i en informasjonskapsel. Vennligst slå på informasjonskapsler på dette domenet. Navnene på informasjonskapslene Anubis lagrer, kan variere uten varsel. Informasjonskapselnavn og -verdier er ikke en del av det offentlege API-et.",
|
||||
"js_context_not_secure": "Du bruker ikke en sikker tilkobling!",
|
||||
"js_context_not_secure_msg": "Prøv å koble til over HTTPS eller fortell administratoren å opprette HTTPS. Se <a hreflang=\"en\" href=\"https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure\">MDN</a> for mer informasjon.",
|
||||
"js_calculating": "Beregner…",
|
||||
"js_missing_feature": "Mangler funksjon",
|
||||
"js_challenge_error": "Utfordringsfeil!",
|
||||
"js_challenge_error_msg": "Mislyktes i å tolke sjekkalgoritmen. Du burde laste inn denne siden på nytt.",
|
||||
"js_calculating_difficulty": "Beregner…<br/>Vanskelighetsnivå:",
|
||||
"js_speed": "Hastighet:",
|
||||
"js_verification_longer": "Verifisering tar lengre enn forventet. Vennligst ikke last inn denne siden på nytt.",
|
||||
"js_success": "Vellykket!",
|
||||
"js_done_took": "Ferdig! Tok",
|
||||
"js_iterations": "gjentakelser",
|
||||
"js_finished_reading": "Jeg har sluttet å lese, fortsett →",
|
||||
"js_calculation_error": "Beregningsfeil!",
|
||||
"js_calculation_error_msg": "Mislyktes i å beregne utfordring:"
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
{
|
||||
"loading": "Lastar inn...",
|
||||
"why_am_i_seeing": "Kvifor ser eg dette?",
|
||||
"protected_by": "Verna av",
|
||||
"protected_from": "Frå",
|
||||
"made_with": "Laga med ❤️ i 🇨🇦",
|
||||
"mascot_design": "Maskotdesign av",
|
||||
"ai_companies_explanation": "Du ser dette av di administratoren av denne nettstaden har sett opp Anubis for å verne sørvaren mot plaga av KI-selskap som aggressivt skrapar nettstader. Dette kan, og held frem med å, forårsake driftstans for nettstadene, som gjer ressursane deira utilgjengelege for alle.",
|
||||
"anubis_compromise": "Anubis er eit kompromiss. Anubis brukar eit «Proof-of-Work»-skjema som liknar på Hashcash, eit liknande skjema for å redusere søppel-e-post. Idéen er at ved småstilte tilfelle er den ytterlegare lastinga ignorerbar, men ved storstilt skraping samlar ho på seg fart og gjer det å skrapa mykje meir dyrt.",
|
||||
"hack_purpose": "Til sjuande og sist er dette ein hack som har som formål å gje ei «god nok» plasshaldarløysing slik at meir tid kan brukast på å fingeravtrykkje og attkjenne hovudlause nettlesarar (f.eks. korleis dei attgjev skrifttypar) slik at utfordringssida ikkje må verte synt til brukarar som er meir sannsynlegvis ekte.",
|
||||
"jshelter_note": "NB: Anubis krev bruk av moderne JavaScript-funksjonar som tillegg som JShelter slår av. Venlegast slå av JShelter eller liknande tillegg for dette domenet.",
|
||||
"version_info": "Denne nettstaden køyrer Anubis-utgåve",
|
||||
"try_again": "Prøv att",
|
||||
"go_home": "Gå heim",
|
||||
"contact_webmaster": "eller om du synest at du ikkje burde vera blokkert, venlegast tak kontakt med administratoren på",
|
||||
"connection_security": "Venlegast vent medan vi stadfestar tryggleiken av tilkoplinga di.",
|
||||
"javascript_required": "Du lyt diverre slå på JavaScript for å koma deg forbi denne utfordringa. Dette krevst av di KI-selskap har endra sosialkontrakten om korleis nettstadsverting fungerer. Ei ikkje-JS-løysing er i gang med å skapast.",
|
||||
"benchmark_requires_js": "JavaScript må vera slegen på for å køyre samanlikningsverktøyet.",
|
||||
"difficulty": "Vanskenivå:",
|
||||
"algorithm": "Algoritme:",
|
||||
"compare": "Jamfør:",
|
||||
"time": "Tid",
|
||||
"iters": "Oppattakingar",
|
||||
"time_a": "Tid A",
|
||||
"iters_a": "Oppattakingar A",
|
||||
"time_b": "Tid B",
|
||||
"iters_b": "Oppattakingar B",
|
||||
"static_check_endpoint": "Dette er berre eit sjekkeendepunkt for din omvende proxy å bruke.",
|
||||
"authorization_required": "Legitimasjon krevst",
|
||||
"cookies_disabled": "Nettlesaren din er konfigurert for å avslå informasjonskapslar. Anubis krev informasjonskapslar for å stadfeste at du er ein ekte brukar. Venlegast slå på informasjonskapslar på dette domenet.",
|
||||
"access_denied": "Tilgang nekta: feilkode",
|
||||
"dronebl_entry": "DroneBL rapporterte ei oppføring.",
|
||||
"see_dronebl_lookup": "sjå",
|
||||
"internal_server_error": "Intern serverfeil: administratoren har feilkonfigurert Anubis. Venlegast tak kontakt med hen og spør hen om å sjå gjennom loggane om",
|
||||
"invalid_redirect": "Ugyldig omdirigering",
|
||||
"redirect_not_parseable": "Omdirigerings-URL-en kunne ikkje tolkast",
|
||||
"redirect_domain_not_allowed": "Omdirigeringsdomenet er ikkje tillate",
|
||||
"failed_to_sign_jwt": "mislukkast i å signere JWT",
|
||||
"invalid_invocation": "Ugyldig framkalling av MakeChallenge",
|
||||
"client_error_browser": "Klientfeil: Venlegast stadfest at nettlesaren din er oppdatert og prøv att seinare.",
|
||||
"oh_noes": "Å nei!",
|
||||
"benchmarking_anubis": "Samanliknar Anubis!",
|
||||
"you_are_not_a_bot": "Du er ikkje ein bot!",
|
||||
"making_sure_not_bot": "Stadfestar at du ikkje er ein bot!",
|
||||
"celphase": "CELPHASE",
|
||||
"js_web_crypto_error": "Nettlesaren din har ikkje eit fungerande web.crypto-element. Ser du dette med ei sikker tilkopling?",
|
||||
"js_web_workers_error": "Nettlesaren din støttar ikkje nettarbeidarar (Anubis brukar dette for å unngå å fryse nettlesaren din). Har du eit tillegg som JShelter installert?",
|
||||
"js_cookies_error": "Nettlesaren lagrar ikkje informasjonskapslar. Anubis brukar informasjonskapslar for å avgjera kva klientar har lukkast i utfordringa ved å lagra ein signert token i ein informasjonskapsel. Venlegast slå på informasjonskapslar på dette domenet. Namna på informasjonskapslane Anubis lagrar, kan variere utan varsel. Informasjonskapselnamn og -verdiar er ikkje ein del av det offentlege API-et.",
|
||||
"js_context_not_secure": "Du brukar ikkje ei sikker tilkopling!",
|
||||
"js_context_not_secure_msg": "Prøv å kople til over HTTPS eller fortel administratoren å opprette HTTPS. Sjå <a hreflang=\"en\" href=\"https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure\">MDN</a> for fleire opplysingar.",
|
||||
"js_calculating": "Reknar…",
|
||||
"js_missing_feature": "Manglar funksjon",
|
||||
"js_challenge_error": "Utfordringsfeil!",
|
||||
"js_challenge_error_msg": "Mislukkast i å tolke sjekkalgoritmen. Du burde laste inn denne sida på nytt.",
|
||||
"js_calculating_difficulty": "Reknar…<br/>Vanskenivå:",
|
||||
"js_speed": "Fart:",
|
||||
"js_verification_longer": "Verifisering tek lengre enn forventa. Venlegast ikkje last inn denne sida på nytt.",
|
||||
"js_success": "Vellykka!",
|
||||
"js_done_took": "Ferdig! Tok",
|
||||
"js_iterations": "oppattakingar",
|
||||
"js_finished_reading": "Eg har slutta å lesa, hald fram →",
|
||||
"js_calculation_error": "Rekningsfeil!",
|
||||
"js_calculation_error_msg": "Mislukkast i å rekne utfordring:"
|
||||
}
|
||||
@@ -2,19 +2,18 @@
|
||||
"loading": "Carregando...",
|
||||
"why_am_i_seeing": "Por que estou vendo isso?",
|
||||
"protected_by": "Protegido por",
|
||||
"protected_from": "de",
|
||||
"made_with": "Feito com ❤️ no Canadá",
|
||||
"made_with": "Feito com ❤️ no 🇨🇦",
|
||||
"mascot_design": "Design do mascote por",
|
||||
"ai_companies_explanation": "Você está vendo isso porque o administrador deste site configurou Anubis para proteger o servidor contra a praga de empresas de IA que realizam captura agressiva dos dados de páginas da Web. Isso pode causar, e de fato causa, indisponibilidade nos sites, o que os torna inacessíveis para todos.",
|
||||
"anubis_compromise": "O Anubis é um meio-termo. Ele utiliza um mecanismo de prova de validação semelhante ao Hashcash, proposto para reduzir spam de e-mail. A ideia é que, para acessos individuais, a carga adicional seja insignificante, mas acessos para captura em massa, ela se acumula e torna esse processo muito mais oneroso.",
|
||||
"hack_purpose": "Por fim, essa é uma solução \"boa o suficiente\" cujo propósito real é gastar mais tempo na identificação de navegadores sem interface (por exemplo: como eles renderizam fontes), para que a página de validação não precise ser apresentada a usuários que têm muito mais probabilidade de serem legítimos.",
|
||||
"jshelter_note": "Lembrando que o Anubis requer o uso de recursos JavaScript modernos, que plugins como o JShelter desabilitarão. Desabilite o JShelter ou similares para este domínio.",
|
||||
"ai_companies_explanation": "Você está vendo isso porque o administrador deste site configurou Anubis para proteger o servidor contra a praga de empresas de IA que realizam scraping agressivo em sites. Isso pode causar, e de fato causa, inoperância nos sites, o que torna seus recursos inacessíveis para todos.",
|
||||
"anubis_compromise": "O Anubis é um meio-termo. Ele utiliza um esquema de Prova de Trabalho (Proof-of-Work) semelhante ao Hashcash, um esquema de Prova de Trabalho proposto para reduzir spam de e-mail. A ideia é que, em escalas individuais, a carga adicional seja insignificante, mas em níveis em massa de scrapers, ela se acumula e torna o scraping muito mais custoso.",
|
||||
"hack_purpose": "Em última análise, este é um hack cujo propósito real é fornecer uma solução \"boa o suficiente\" para que mais tempo possa ser gasto na identificação de navegadores sem interface (por exemplo: por meio de como eles fazem a renderização de fontes), para que a página do desafio da prova de trabalho não precise ser apresentada a usuários que têm muito mais probabilidade de serem legítimos.",
|
||||
"jshelter_note": "Observe que o Anubis requer o uso de recursos JavaScript modernos que plugins como o JShelter desabilitarão. Desabilite o JShelter ou outros plugins semelhantes para este domínio.",
|
||||
"version_info": "Este site está usando o Anubis versão",
|
||||
"try_again": "Tente novamente",
|
||||
"go_home": "Início",
|
||||
"contact_webmaster": "ou se você acredita que não deveria estar bloqueado, contate o administrador em",
|
||||
"contact_webmaster": "ou se você acredita que não deveria estar bloqueado, contate o webmaster em",
|
||||
"connection_security": "Por favor, aguarde um momento enquanto nós garantimos a segurança de sua conexão.",
|
||||
"javascript_required": "Infelizmente, você deve habilitar JavaScript para passar por esta validação. Isso é necessário porque empresas de IA alteraram o contrato social sobre como a hospedagem de sites funciona. Uma solução não dependente de JavaScript ainda está sendo desenvolvida.",
|
||||
"javascript_required": "Infelizmente, você deve habilitar JavaScript para passar por este desafio. Isso é necessário porque empresas de IA alteraram o contrato social sobre como a hospedagem de sites funciona. Uma solução que não use JavaScript ainda está sendo desenvolvida.",
|
||||
"benchmark_requires_js": "Para executar a ferramenta de benchmark, é necessário que o JavaScript esteja habilitado.",
|
||||
"difficulty": "Dificuldade:",
|
||||
"algorithm": "Algoritmo:",
|
||||
@@ -27,7 +26,7 @@
|
||||
"iters_b": "Iteração B",
|
||||
"static_check_endpoint": "Este é apenas um ponto de verificação para seu proxy reverso usar.",
|
||||
"authorization_required": "Autorização necessária",
|
||||
"cookies_disabled": "Seu navegador está configurado para desabilitar cookies. O Anubis requer cookies somente com o interesse de garantir que você seja um cliente válido. Por favor, habilite os cookies para este domínio.",
|
||||
"cookies_disabled": "Seu navegador está configurado para desabilitar cookies. O Anubis requer cookies para o interesse legítimo de garantir que você seja um cliente válido. Habilite os cookies para este domínio.",
|
||||
"access_denied": "Acesso negado: código de erro",
|
||||
"dronebl_entry": "DroneBL relatou uma entrada",
|
||||
"see_dronebl_lookup": "consulte",
|
||||
@@ -36,21 +35,21 @@
|
||||
"redirect_not_parseable": "URL de redirecionamento não analisável",
|
||||
"redirect_domain_not_allowed": "Domínio de redirecionamento não permitido",
|
||||
"failed_to_sign_jwt": "falha ao assinar JWT",
|
||||
"invalid_invocation": "Invocação de MakeChallenge inválida",
|
||||
"client_error_browser": "Erro do cliente: verifique se seu navegador está atualizado e tente novamente mais tarde.",
|
||||
"invalid_invocation": "Invocação inválida de MakeChallenge",
|
||||
"client_error_browser": "Erro do cliente: verifique se seu navegador está atualizado e tente novamente mais tarde..",
|
||||
"oh_noes": "Ah, não!",
|
||||
"benchmarking_anubis": "Fazendo benchmark do Anubis!",
|
||||
"you_are_not_a_bot": "Você não é um bot!",
|
||||
"making_sure_not_bot": "Certificando de que você não é um bot!",
|
||||
"celphase": "CELPHASE",
|
||||
"js_web_crypto_error": "Seu navegador não possui um elemento web.crypto funcional. Você está visualizando isso em um contexto seguro?",
|
||||
"js_web_workers_error": "Seu navegador não suporta web workers (o Anubis os usa para evitar que seu navegador trave). Você tem um plugin como o JShelter instalado?",
|
||||
"js_cookies_error": "Seu navegador não armazena cookies. O Anubis usa cookies para determinar quais clientes passaram pelas validações, armazenando um token assinado nesse cookie. Habilite o armazenamento de cookies para este domínio. Os nomes dos cookies armazenados pelo Anubis podem variar sem aviso prévio. Os nomes e valores dos cookies não fazem parte da API pública.",
|
||||
"js_web_workers_error": "Seu navegador não oferece suporte a web workers (o Anubis usa isso para evitar que seu navegador trave). Você tem um plugin como o JShelter instalado?",
|
||||
"js_cookies_error": "Seu navegador não armazena cookies. O Anubis usa cookies para determinar quais clientes passaram nos desafios, armazenando um token assinado em um cookie. Habilite o armazenamento de cookies para este domínio. Os nomes dos cookies armazenados pelo Anubis podem variar sem aviso prévio. Os nomes e valores dos cookies não fazem parte da API pública.",
|
||||
"js_context_not_secure": "Seu contexto não é seguro!",
|
||||
"js_context_not_secure_msg": "Tente conectar-se via HTTPS ou avise o administrador para configurar a segurança de site via HTTPS. Para mais informações, consulte o <a href=\"https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure\">MDN</a>.",
|
||||
"js_context_not_secure_msg": "Tente conectar-se via HTTPS ou avise o administrador para configurar o HTTPS. Para mais informações, consulte o <a href=\"https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure\">MDN</a>.",
|
||||
"js_calculating": "Calculando...",
|
||||
"js_missing_feature": "Recurso não disponível",
|
||||
"js_challenge_error": "Erro na validação!",
|
||||
"js_missing_feature": "Faltando recurso",
|
||||
"js_challenge_error": "Erro no desafio!",
|
||||
"js_challenge_error_msg": "Falha ao resolver o algoritmo de verificação. Talvez seja necessário recarregar a página.",
|
||||
"js_calculating_difficulty": "Calculando...<br/>Dificuldade:",
|
||||
"js_speed": "Velocidade:",
|
||||
@@ -60,5 +59,5 @@
|
||||
"js_iterations": "iterações",
|
||||
"js_finished_reading": "Terminei de ler, continue →",
|
||||
"js_calculation_error": "Erro de cálculo!",
|
||||
"js_calculation_error_msg": "Falha ao calcular a validação:"
|
||||
"js_calculation_error_msg": "Falha ao calcular o desafio:"
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
{
|
||||
"loading": "Загрузка...",
|
||||
"why_am_i_seeing": "Почему я вижу это?",
|
||||
"protected_by": "Защищено",
|
||||
"protected_from": "От",
|
||||
"made_with": "Сделано с ❤️ из 🇨🇦",
|
||||
"mascot_design": "Дизайн маскота от",
|
||||
"ai_companies_explanation": "Вы это видите, потому что администратор этого сайта настроил Anubis для защиты сервера от атак, использующих ИИ, которые агрессивно копируют данные с сайтов. Это может привести к зависанию сайтов и делает их ресурсы недоступными для всех.",
|
||||
"anubis_compromise": "Anubis - это компромисс. Anubis использует Proof-of-Work, похожую на Hashcash, для борьбы со спамом в электронной почте. Идея в том, что на отдельных уровнях дополнительная нагрузка не влиятельна, но на уровне массового парсинга она накапливается и значительно удорожает сбор данных.",
|
||||
"hack_purpose": "В реальности, это хак, у которого настоящая цель - предоставить «достаточно хорошее» решение, чтобы можно было потратить больше времени на идентификацию headless-браузеров (например, по тому, как они выполняют отрисовку шрифтов), чтобы не отображать страницу с подтверждением пользователям, которые с гораздо большей вероятностью являются настоящими.",
|
||||
"jshelter_note": "Anubis требует использования современных функций JavaScript, которые плагины, по типу JShelter, отключают. Пожалуйста, отключите JShelter и другие подобные плагины для этого домена.",
|
||||
"version_info": "На сайте запущен Anubis версии",
|
||||
"try_again": "Попробуйте снова",
|
||||
"go_home": "Перейти на домашнюю",
|
||||
"contact_webmaster": "если вы уверены, что это ошибка, свяжитесь с владельцем сайта через",
|
||||
"connection_security": "Пожалуйста, подождите, пока мы проверим безопасность вашего соединения.",
|
||||
"javascript_required": "К сожалению, для решения этой проверки необходимо включить JavaScript. Это необходимо, поскольку компании, занимающиеся разработкой ИИ, изменили моральные правила, касающийся хостинга веб-сайтов. Решение без использования JavaScript находится в стадии разработки.",
|
||||
"benchmark_requires_js": "Для работы тестирования необходимо включить JavaScript.",
|
||||
"difficulty": "Сложность:",
|
||||
"algorithm": "Алгоритм:",
|
||||
"compare": "Сравнить:",
|
||||
"time": "Время",
|
||||
"iters": "Итерации",
|
||||
"time_a": "Время A",
|
||||
"iters_a": "Итерации A",
|
||||
"time_b": "Время B",
|
||||
"iters_b": "Итерации B",
|
||||
"static_check_endpoint": "Это всего лишь точка проверки, которую может использовать ваш обратный прокси-сервер.",
|
||||
"authorization_required": "Требуется авторизация.",
|
||||
"cookies_disabled": "В вашем браузере отключены cookie файлы. Anubis требует их для подтверждения того, что вы являетесь настоящим человеком. Пожалуйста, включите файлы cookie для этого домена",
|
||||
"access_denied": "Доступ запрещён: код ошибки",
|
||||
"dronebl_entry": "DroneBL сообщил о записи",
|
||||
"see_dronebl_lookup": "см.",
|
||||
"internal_server_error": "Внутренняя ошибка сервера: администратор неправильно настроил Anubis. Обратитесь к администратору и попросите его просмотреть логи",
|
||||
"invalid_redirect": "Неверное перенаправление",
|
||||
"redirect_not_parseable": "URL-адрес перенаправления не может быть анализирован",
|
||||
"redirect_domain_not_allowed": "Перенаправление домена запрещено",
|
||||
"failed_to_sign_jwt": "не смог подписать JWT",
|
||||
"invalid_invocation": "Неверный вызов MakeChallenge",
|
||||
"client_error_browser": "Ошибка клиента: убедитесь, что у вас браузер новейшей версии, и повторите попытку позже.",
|
||||
"oh_noes": "О нет!",
|
||||
"benchmarking_anubis": "Анализ Анубиса!",
|
||||
"you_are_not_a_bot": "Вы не бот!",
|
||||
"making_sure_not_bot": "Проверяем, что вы не бот!",
|
||||
"celphase": "CELPHASE",
|
||||
"js_web_crypto_error": "В вашем браузере отсутствует функция web.crypto. Вы просматриваете страницу через защищённый контекст?",
|
||||
"js_web_workers_error": "Ваш браузер не поддерживает web worker (Anubis использует его, чтобы избежать зависания браузера). У вас установлен плагин типа JShelter?",
|
||||
"js_cookies_error": "Ваш браузер не сохраняет cookie файлы. Anubis использует их для определения клиентов, прошедших проверку, сохраняя подписанный токен в файле cookie. Включите сохранение файлов cookie для этого домена. Имена файлов cookie, хранимых Anubis, могут изменяться без предварительного уведомления. Имена и значения cookie файлов не являются частью общедоступного API.",
|
||||
"js_context_not_secure": "Ваш контекст небезопасен!",
|
||||
"js_context_not_secure_msg": "Попробуйте подключиться по HTTPS или попросите администратора, чтобы он настроил HTTPS. Подробнее см. <a href=\"https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure\">MDN</a>.",
|
||||
"js_calculating": "Расчёт...",
|
||||
"js_missing_feature": "Отсутствует функция",
|
||||
"js_challenge_error": "Ошибка проверки!",
|
||||
"js_challenge_error_msg": "Не удалось определить алгоритм проверки. Возможно, нужно перезагрузить страницу..",
|
||||
"js_calculating_difficulty": "Расчёт...<br/>Сложность:",
|
||||
"js_speed": "Скорость:",
|
||||
"js_verification_longer": "Проверка занимает больше времени, чем ожидалось. Пожалуйста, не обновляйте страницу.",
|
||||
"js_success": "Успех!",
|
||||
"js_done_took": "Получилось! Заняло",
|
||||
"js_iterations": "итераций",
|
||||
"js_finished_reading": "Я дочитал, продолжить →",
|
||||
"js_calculation_error": "Ошибка расчёта!",
|
||||
"js_calculation_error_msg": "Не удалось рассчитать задачу:"
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
"loading": "Yükleniyor...",
|
||||
"why_am_i_seeing": "Bunu neden görüyorum?",
|
||||
"protected_by": "Koruma sağlayan:",
|
||||
"protected_from": "From",
|
||||
"made_with": "🇨🇦'da ❤️ ile yapıldı",
|
||||
"mascot_design": "Maskot tasarımı:",
|
||||
"ai_companies_explanation": "Bunu görüyorsunuz çünkü bu web sitesinin yöneticisi, yapay zeka şirketlerinin web sitelerini agresif şekilde kazımasına karşı sunucuyu korumak için Anubis'i kurdu. Bu tarz kazımalar sitelerin erişilemez olmasına ve kesintilere neden olabiliyor.",
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
{
|
||||
"loading": "加载中...",
|
||||
"why_am_i_seeing": "为什么我会看到这个?",
|
||||
"protected_by": "本网站由",
|
||||
"protected_from": "保护,来自",
|
||||
"made_with": "在 🇨🇦 用 ❤️ 制作",
|
||||
"mascot_design": "吉祥物由",
|
||||
"ai_companies_explanation": "您会看到这个画面,是因为网站管理员启用了 Anubis 来保护服务器,避免 AI 公司大量爬取网站内容。这类行为会导致网站崩溃,让所有用户都无法正常访问资源。",
|
||||
"anubis_compromise": "Anubis 是一种折中做法。它采用了类似 Hashcash 的工作量证明机制(Proof-of-Work),该机制最初是为了减少垃圾邮件而提出。其核心概念是:对个别用户而言,额外的计算负担可以忽略,但对大规模爬虫来说,累积起来的成本将大幅增加,从而让爬取行为变得更困难。",
|
||||
"hack_purpose": "本质上,这是一种权宜的解法,目的是提供一个“够用”的暂时性防护措施,好让开发者有更多时间针对无头浏览器进行指纹特征识别(例如:分析其字体渲染方式),以便未来不再需要对可能为合法用户的访客展示工作量证明页面。",
|
||||
"jshelter_note": "请注意,Anubis 需要使用现代 JavaScript 功能,而像 JShelter 这类插件可能会阻挡这些功能。请为此域名停用 JShelter 或类似的插件。",
|
||||
"version_info": "这个网站正在运行的 Anubis 版本为",
|
||||
"try_again": "再试一次",
|
||||
"go_home": "返回首页",
|
||||
"contact_webmaster": "或者您觉得您不应该被封锁,请联系网站管理员于",
|
||||
"connection_security": "请稍等,我们需要在继续之前检查您的连接安全性。",
|
||||
"javascript_required": "很遗憾,您必须启用 JavaScript 才能通过这项验证。这是因为 AI 公司已经改变了网站托管的社会契约,因此我们必须采取这样的保护机制。无需 JavaScript 的解决方案仍在开发中。",
|
||||
"benchmark_requires_js": "运行基准测试工具需要启用 JavaScript。",
|
||||
"difficulty": "难度:",
|
||||
"algorithm": "算法:",
|
||||
"compare": "比较:",
|
||||
"time": "时间",
|
||||
"iters": "迭代",
|
||||
"time_a": "时间 A",
|
||||
"iters_a": "迭代 A",
|
||||
"time_b": "时间 B",
|
||||
"iters_b": "迭代 B",
|
||||
"static_check_endpoint": "这是提供给您的反向代理服务器使用的检查端点。",
|
||||
"authorization_required": "需要认证",
|
||||
"cookies_disabled": "您的浏览器目前已禁用 Cookie,为了确认您是合法用户,Anubis 需要启用 Cookie。 请您为此域名启用 Cookie",
|
||||
"access_denied": "拒绝访问:错误代码",
|
||||
"dronebl_entry": "DroneBL 报告了一条记录",
|
||||
"see_dronebl_lookup": "见",
|
||||
"internal_server_error": "内部服务器错误:管理员错误地配置了 Anubis。 请联系管理员要求他们检查日志",
|
||||
"invalid_redirect": "无效的重定向",
|
||||
"redirect_not_parseable": "重定向 URL 无法解析",
|
||||
"redirect_domain_not_allowed": "重定向的域名并不允许",
|
||||
"failed_to_sign_jwt": "签署 JWT 失败",
|
||||
"invalid_invocation": "无效的 MakeChallenge 调用",
|
||||
"client_error_browser": "客户端错误:请确保您的浏览器是最新版本并稍候再试。",
|
||||
"oh_noes": "哎呀糟糕了!",
|
||||
"benchmarking_anubis": "正在进行 Anubis 性能测试!",
|
||||
"you_are_not_a_bot": "你不是机器人!",
|
||||
"making_sure_not_bot": "正在确认你是不是机器人!",
|
||||
"celphase": "CELPHASE 设计",
|
||||
"js_web_crypto_error": "您的浏览器无法正常使用 web.crypto 组件。您是否通过安全连接(HTTPS)查看此网站?",
|
||||
"js_web_workers_error": "您的浏览器并不支持 Web workers (Anubis 使用这个来避免冻结您的浏览器 )您有安装像是 JShelter 之类的插件吗?",
|
||||
"js_cookies_error": "您的浏览器无法存储 Cookie。 Anubis 会使用 Cookie 存储签署的凭证,以判断用户是否已通过验证。请为此域名启用 Cookie 存储功能。 请注意,Anubis 存储的 Cookie 名称可能会变动,且其名称与内容不属于公开 API 的一部分。",
|
||||
"js_context_not_secure": "您的内容并不安全",
|
||||
"js_context_not_secure_msg": "请尝试使用 HTTPS 连接,或联系网站管理员设置 HTTPS。更多信息请参见 <a href=\"https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure\">MDN</a>。",
|
||||
"js_calculating": "计算中...",
|
||||
"js_missing_feature": "缺少功能",
|
||||
"js_challenge_error": "挑战错误!",
|
||||
"js_challenge_error_msg": "解决检查算法失败。 您可能会想要刷新页面。",
|
||||
"js_calculating_difficulty": "计算中...<br/>难度:",
|
||||
"js_speed": "速度:",
|
||||
"js_verification_longer": "验证所花的时间高于预期。 请不要刷新页面。",
|
||||
"js_success": "成功!",
|
||||
"js_done_took": "完成! 花费",
|
||||
"js_iterations": "迭代",
|
||||
"js_finished_reading": "我读完了,继续 →",
|
||||
"js_calculation_error": "计算错误!",
|
||||
"js_calculation_error_msg": "计算挑战失败:"
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
{
|
||||
"loading": "載入中...",
|
||||
"why_am_i_seeing": "為什麼我看到這個?",
|
||||
"protected_by": "本網站由",
|
||||
"protected_from": "保護,來自",
|
||||
"made_with": "在 🇨🇦 用 ❤️ 製作",
|
||||
"mascot_design": "吉祥物由",
|
||||
"ai_companies_explanation": "您會看到這個畫面,是因為網站管理員啟用了 Anubis 來保護伺服器,避免 AI 公司大量爬取網站內容。這類行為會導致網站當機,讓所有使用者都無法正常存取資源。",
|
||||
"anubis_compromise": "Anubis 是一種折衷做法。它採用了類似 Hashcash 的工作量證明機制(Proof-of-Work),該機制最初是為了減少垃圾郵件而提出。其核心概念是:對個別使用者而言,額外的運算負擔可以忽略,但對大規模爬蟲來說,累積起來的成本將大幅增加,從而讓爬取行為變得更困難。",
|
||||
"hack_purpose": "本質上,這是一種權宜的解法,目的是提供一個「夠用」的暫時性防護措施,好讓開發者有更多時間針對無頭瀏覽器進行指紋特徵辨識(例如:分析其字型渲染方式),以便未來不再需要對可能為合法使用者的訪客展示工作量證明頁面。",
|
||||
"jshelter_note": "請注意,Anubis 需要使用現代 JavaScript 功能,而像 JShelter 這類外掛可能會阻擋這些功能。請為此網域停用 JShelter 或類似的插件。",
|
||||
"version_info": "這個網站正在運行 Anubis 版本",
|
||||
"try_again": "再試一次",
|
||||
"go_home": "回首頁",
|
||||
"contact_webmaster": "或者您覺得您不應該被封鎖,請聯絡站點管理員於",
|
||||
"connection_security": "請稍等,我們需要在繼續之前檢閱您的連線安全性。",
|
||||
"javascript_required": "很遺憾,您必須啟用 JavaScript 才能通過這項驗證。這是因為 AI 公司已經改變了網站託管的社會契約,因此我們必須採取這樣的保護機制。無需 JavaScript 的解法仍在開發中。",
|
||||
"benchmark_requires_js": "執行基準測試工具需要啟用 JavaScript。",
|
||||
"difficulty": "難度:",
|
||||
"algorithm": "演算法:",
|
||||
"compare": "比較:",
|
||||
"time": "時間",
|
||||
"iters": "迭代",
|
||||
"time_a": "時間 A",
|
||||
"iters_a": "迭代 A",
|
||||
"time_b": "時間 B",
|
||||
"iters_b": "迭代 B",
|
||||
"static_check_endpoint": "這是提供給您的反向代理伺服器使用的檢查端點。",
|
||||
"authorization_required": "需要認證",
|
||||
"cookies_disabled": "您的瀏覽器目前已停用 Cookie,為了確認您是合法使用者,Anubis 需要啟用 Cookie。 請為此網域啟用 Cookie",
|
||||
"access_denied": "拒絕存取:錯誤代碼",
|
||||
"dronebl_entry": "DroneBL 回報了一筆紀錄",
|
||||
"see_dronebl_lookup": "見",
|
||||
"internal_server_error": "內部伺服器錯誤:管理員錯誤地配置了 Anubis。 請聯絡管理員要求他們檢閱日誌",
|
||||
"invalid_redirect": "無效的重新導向",
|
||||
"redirect_not_parseable": "重新導向 URL 無法解析",
|
||||
"redirect_domain_not_allowed": "重新導向的網域並不允許",
|
||||
"failed_to_sign_jwt": "簽署 JWT 失敗",
|
||||
"invalid_invocation": "無效的 MakeChallenge 呼叫",
|
||||
"client_error_browser": "客戶端錯誤:請確保您的瀏覽器是最新版本並稍候再試。",
|
||||
"oh_noes": "哎呀糟糕了!",
|
||||
"benchmarking_anubis": "正在進行 Anubis 效能測試!",
|
||||
"you_are_not_a_bot": "你不是機器人!",
|
||||
"making_sure_not_bot": "正在確認你是不是機器人!",
|
||||
"celphase": "CELPHASE 設計",
|
||||
"js_web_crypto_error": "您的瀏覽器無法正常使用 web.crypto 元件。您是否透過安全連線(HTTPS)檢視此網站?",
|
||||
"js_web_workers_error": "您的瀏覽器並不支援 Web workers (Anubis 使用這個來避免凍結您的瀏覽器 )您有安裝像是 JShelter 之類的插件嗎?",
|
||||
"js_cookies_error": "您的瀏覽器無法儲存 Cookie。 Anubis 會使用 Cookie 儲存簽署的憑證,以判斷使用者是否已通過驗證。請為此網域啟用 Cookie 儲存功能。 請注意,Anubis 儲存的 Cookie 名稱可能會變動,且其名稱與內容不屬於公開 API 的一部分。",
|
||||
"js_context_not_secure": "您的內容並不安全",
|
||||
"js_context_not_secure_msg": "請嘗試使用 HTTPS 連線,或聯繫網站管理員設定 HTTPS。更多資訊請參見 <a href=\"https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure\">MDN</a>。",
|
||||
"js_calculating": "計算中...",
|
||||
"js_missing_feature": "缺少功能",
|
||||
"js_challenge_error": "挑戰錯誤!",
|
||||
"js_challenge_error_msg": "解決檢查演算法失敗。 您可能會想要重整頁面。",
|
||||
"js_calculating_difficulty": "計算中...<br/>難度:",
|
||||
"js_speed": "速度:",
|
||||
"js_verification_longer": "驗證所花的時間高於預期。 請不要重整頁面。",
|
||||
"js_success": "成功!",
|
||||
"js_done_took": "完成! 花費",
|
||||
"js_iterations": "迭代",
|
||||
"js_finished_reading": "我讀完了,繼續 →",
|
||||
"js_calculation_error": "計算錯誤!",
|
||||
"js_calculation_error_msg": "計算挑戰失敗:"
|
||||
}
|
||||
@@ -3,11 +3,11 @@ package localization
|
||||
import (
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"github.com/TecharoHQ/anubis"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/TecharoHQ/anubis"
|
||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
@@ -94,15 +94,6 @@ func (sl *SimpleLocalizer) T(messageID string) string {
|
||||
return sl.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: messageID})
|
||||
}
|
||||
|
||||
// Get the language that is used by the localizer by retrieving a well-known string that is required to be present
|
||||
func (sl *SimpleLocalizer) GetLang() string {
|
||||
_, tag, err := sl.Localizer.LocalizeWithTag(&i18n.LocalizeConfig{MessageID: "loading"})
|
||||
if err != nil {
|
||||
return "en"
|
||||
}
|
||||
return tag.String()
|
||||
}
|
||||
|
||||
// GetLocalizer creates a localizer based on the request's Accept-Language header or forcedLanguage option
|
||||
func GetLocalizer(r *http.Request) *SimpleLocalizer {
|
||||
var localizer *i18n.Localizer
|
||||
|
||||
@@ -2,7 +2,6 @@ package localization
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
@@ -12,64 +11,86 @@ import (
|
||||
func TestLocalizationService(t *testing.T) {
|
||||
service := NewLocalizationService()
|
||||
|
||||
loadingStrMap := map[string]string{
|
||||
"de": "Ladevorgang...",
|
||||
"en": "Loading...",
|
||||
"es": "Cargando...",
|
||||
"et": "Laadin...",
|
||||
"fil": "Naglo-load...",
|
||||
"fr": "Chargement...",
|
||||
"ja": "ロード中...",
|
||||
"is": "Hleður...",
|
||||
"nb": "Laster inn...",
|
||||
"nn": "Lastar inn...",
|
||||
"pt-BR": "Carregando...",
|
||||
"tr": "Yükleniyor...",
|
||||
"ru": "Загрузка...",
|
||||
"zh-CN": "加载中...",
|
||||
"zh-TW": "載入中...",
|
||||
}
|
||||
t.Run("English localization", func(t *testing.T) {
|
||||
localizer := service.GetLocalizer("en")
|
||||
result := localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "loading"})
|
||||
if result != "Loading..." {
|
||||
t.Errorf("Expected 'Loading...', got '%s'", result)
|
||||
}
|
||||
})
|
||||
|
||||
var keys []string
|
||||
t.Run("French localization", func(t *testing.T) {
|
||||
localizer := service.GetLocalizer("fr")
|
||||
result := localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "loading"})
|
||||
if result != "Chargement..." {
|
||||
t.Errorf("Expected 'Chargement...', got '%s'", result)
|
||||
}
|
||||
})
|
||||
|
||||
for lang := range loadingStrMap {
|
||||
keys = append(keys, lang)
|
||||
}
|
||||
t.Run("German localization", func(t *testing.T) {
|
||||
localizer := service.GetLocalizer("de")
|
||||
result := localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "loading"})
|
||||
if result != "Ladevorgang..." {
|
||||
t.Errorf("Expected 'Ladevorgang...', got '%s'", result)
|
||||
}
|
||||
})
|
||||
|
||||
sort.Strings(keys)
|
||||
t.Run("Turkish localization", func(t *testing.T) {
|
||||
localizer := service.GetLocalizer("tr")
|
||||
result := localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "loading"})
|
||||
if result != "Yükleniyor..." {
|
||||
t.Errorf("Expected 'Yükleniyor...', got '%s'", result)
|
||||
}
|
||||
})
|
||||
|
||||
for _, lang := range keys {
|
||||
expected := loadingStrMap[lang]
|
||||
t.Run(fmt.Sprintf("%s localization", lang), func(t *testing.T) {
|
||||
localizer := service.GetLocalizer(lang)
|
||||
result := localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "loading"})
|
||||
if result != expected {
|
||||
t.Errorf("Expected '%s', got '%s'", expected, result)
|
||||
t.Run("All required keys exist in English", func(t *testing.T) {
|
||||
localizer := service.GetLocalizer("en")
|
||||
requiredKeys := []string{
|
||||
"loading", "why_am_i_seeing", "protected_by", "made_with",
|
||||
"mascot_design", "try_again", "go_home", "javascript_required",
|
||||
}
|
||||
|
||||
for _, key := range requiredKeys {
|
||||
result := localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: key})
|
||||
if result == "" {
|
||||
t.Errorf("Key '%s' returned empty string", key)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Test for requiredKeys localization
|
||||
requiredKeys := []string{
|
||||
"loading", "why_am_i_seeing", "protected_by", "protected_from", "made_with",
|
||||
"mascot_design", "try_again", "go_home", "javascript_required",
|
||||
}
|
||||
t.Run("All required keys exist in French", func(t *testing.T) {
|
||||
localizer := service.GetLocalizer("fr")
|
||||
requiredKeys := []string{
|
||||
"loading", "why_am_i_seeing", "protected_by", "made_with",
|
||||
"mascot_design", "try_again", "go_home", "javascript_required",
|
||||
}
|
||||
|
||||
for _, lang := range keys {
|
||||
t.Run(fmt.Sprintf("All required keys exist in %s", lang), func(t *testing.T) {
|
||||
loc := service.GetLocalizer(lang)
|
||||
for _, key := range requiredKeys {
|
||||
result := loc.MustLocalize(&i18n.LocalizeConfig{MessageID: key})
|
||||
if result == "" {
|
||||
t.Errorf("Key '%s' returned empty string", key)
|
||||
}
|
||||
for _, key := range requiredKeys {
|
||||
result := localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: key})
|
||||
if result == "" {
|
||||
t.Errorf("Key '%s' returned empty string", key)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("All required keys exist in Turkish", func(t *testing.T) {
|
||||
localizer := service.GetLocalizer("tr")
|
||||
requiredKeys := []string{
|
||||
"loading", "why_am_i_seeing", "protected_by", "made_with",
|
||||
"mascot_design", "try_again", "go_home", "javascript_required",
|
||||
}
|
||||
|
||||
for _, key := range requiredKeys {
|
||||
result := localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: key})
|
||||
if result == "" {
|
||||
t.Errorf("Key '%s' returned empty string", key)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type manifest struct {
|
||||
SupportedLanguages []string `json:"supportedLanguages"`
|
||||
SupportedLanguages []string `json:"supported_languages"`
|
||||
}
|
||||
|
||||
func loadManifest(t *testing.T) manifest {
|
||||
@@ -110,19 +131,10 @@ func TestComprehensiveTranslations(t *testing.T) {
|
||||
|
||||
sort.Strings(keys)
|
||||
|
||||
manifest := loadManifest(t)
|
||||
if len(manifest.SupportedLanguages) == 0 {
|
||||
t.Fatal("no languages loaded")
|
||||
}
|
||||
|
||||
for _, lang := range loadManifest(t).SupportedLanguages {
|
||||
t.Run(lang, func(t *testing.T) {
|
||||
loc := service.GetLocalizer(lang)
|
||||
sl := SimpleLocalizer{Localizer: loc}
|
||||
service_lang := sl.GetLang()
|
||||
if service_lang != lang {
|
||||
t.Error("Localizer language not same as specified")
|
||||
}
|
||||
for _, key := range keys {
|
||||
t.Run(key, func(t *testing.T) {
|
||||
if result := sl.T(key); result == "" {
|
||||
|
||||
@@ -73,12 +73,6 @@ func (cr *CELRequest) ResolveName(name string) (any, bool) {
|
||||
return expressions.URLValues{Values: cr.URL.Query()}, true
|
||||
case "headers":
|
||||
return expressions.HTTPHeaders{Header: cr.Header}, true
|
||||
case "load_1m":
|
||||
return expressions.Load1(), true
|
||||
case "load_5m":
|
||||
return expressions.Load5(), true
|
||||
case "load_15m":
|
||||
return expressions.Load15(), true
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"github.com/google/cel-go/cel"
|
||||
"github.com/google/cel-go/common/types"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
"github.com/google/cel-go/common/types/traits"
|
||||
"github.com/google/cel-go/ext"
|
||||
)
|
||||
|
||||
@@ -24,36 +23,6 @@ func BotEnvironment() (*cel.Env, error) {
|
||||
cel.Variable("path", cel.StringType),
|
||||
cel.Variable("query", cel.MapType(cel.StringType, cel.StringType)),
|
||||
cel.Variable("headers", cel.MapType(cel.StringType, cel.StringType)),
|
||||
cel.Variable("load_1m", cel.DoubleType),
|
||||
cel.Variable("load_5m", cel.DoubleType),
|
||||
cel.Variable("load_15m", cel.DoubleType),
|
||||
|
||||
// Bot-specific functions:
|
||||
cel.Function("missingHeader",
|
||||
cel.Overload("missingHeader_map_string_string_string",
|
||||
[]*cel.Type{cel.MapType(cel.StringType, cel.StringType), cel.StringType},
|
||||
cel.BoolType,
|
||||
cel.BinaryBinding(func(headers, key ref.Val) ref.Val {
|
||||
// Convert headers to a trait that supports Find
|
||||
headersMap, ok := headers.(traits.Indexer)
|
||||
if !ok {
|
||||
return types.ValOrErr(headers, "headers is not a map, but is %T", headers)
|
||||
}
|
||||
|
||||
keyStr, ok := key.(types.String)
|
||||
if !ok {
|
||||
return types.ValOrErr(key, "key is not a string, but is %T", key)
|
||||
}
|
||||
|
||||
val := headersMap.Get(keyStr)
|
||||
// Check if the key is missing by testing for an error
|
||||
if types.IsError(val) {
|
||||
return types.Bool(true) // header is missing
|
||||
}
|
||||
return types.Bool(false) // header is present
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,269 +0,0 @@
|
||||
package expressions
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/cel-go/common/types"
|
||||
)
|
||||
|
||||
func TestBotEnvironment(t *testing.T) {
|
||||
env, err := BotEnvironment()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create bot environment: %v", err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
expression string
|
||||
headers map[string]string
|
||||
expected types.Bool
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "missing-header",
|
||||
expression: `missingHeader(headers, "Missing-Header")`,
|
||||
headers: map[string]string{
|
||||
"User-Agent": "test-agent",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
expected: types.Bool(true),
|
||||
description: "should return true when header is missing",
|
||||
},
|
||||
{
|
||||
name: "existing-header",
|
||||
expression: `missingHeader(headers, "User-Agent")`,
|
||||
headers: map[string]string{
|
||||
"User-Agent": "test-agent",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
expected: types.Bool(false),
|
||||
description: "should return false when header exists",
|
||||
},
|
||||
{
|
||||
name: "case-sensitive",
|
||||
expression: `missingHeader(headers, "user-agent")`,
|
||||
headers: map[string]string{
|
||||
"User-Agent": "test-agent",
|
||||
},
|
||||
expected: types.Bool(true),
|
||||
description: "should be case-sensitive (user-agent != User-Agent)",
|
||||
},
|
||||
{
|
||||
name: "empty-headers",
|
||||
expression: `missingHeader(headers, "Any-Header")`,
|
||||
headers: map[string]string{},
|
||||
expected: types.Bool(true),
|
||||
description: "should return true for any header when map is empty",
|
||||
},
|
||||
{
|
||||
name: "real-world-sec-ch-ua",
|
||||
expression: `missingHeader(headers, "Sec-Ch-Ua")`,
|
||||
headers: map[string]string{
|
||||
"User-Agent": "curl/7.68.0",
|
||||
"Accept": "*/*",
|
||||
"Host": "example.com",
|
||||
},
|
||||
expected: types.Bool(true),
|
||||
description: "should detect missing browser-specific headers from bots",
|
||||
},
|
||||
{
|
||||
name: "browser-with-sec-ch-ua",
|
||||
expression: `missingHeader(headers, "Sec-Ch-Ua")`,
|
||||
headers: map[string]string{
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
||||
"Sec-Ch-Ua": `"Chrome"; v="91", "Not A Brand"; v="99"`,
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
||||
},
|
||||
expected: types.Bool(false),
|
||||
description: "should return false when browser sends Sec-Ch-Ua header",
|
||||
},
|
||||
}
|
||||
|
||||
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{}{
|
||||
"headers": tt.headers,
|
||||
})
|
||||
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 := `missingHeader(headers, "Test-Header")`
|
||||
_, err := Compile(env, src)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to compile missingHeader expression: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestThresholdEnvironment(t *testing.T) {
|
||||
env, err := ThresholdEnvironment()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create threshold environment: %v", err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
expression string
|
||||
variables map[string]interface{}
|
||||
expected types.Bool
|
||||
description string
|
||||
shouldCompile bool
|
||||
}{
|
||||
{
|
||||
name: "weight-variable-available",
|
||||
expression: `weight > 100`,
|
||||
variables: map[string]interface{}{"weight": 150},
|
||||
expected: types.Bool(true),
|
||||
description: "should support weight variable in expressions",
|
||||
shouldCompile: true,
|
||||
},
|
||||
{
|
||||
name: "weight-variable-false-case",
|
||||
expression: `weight > 100`,
|
||||
variables: map[string]interface{}{"weight": 50},
|
||||
expected: types.Bool(false),
|
||||
description: "should correctly evaluate weight comparisons",
|
||||
shouldCompile: true,
|
||||
},
|
||||
{
|
||||
name: "missingHeader-not-available",
|
||||
expression: `missingHeader(headers, "Test")`,
|
||||
variables: map[string]interface{}{},
|
||||
expected: types.Bool(false), // not used
|
||||
description: "should not have missingHeader function available",
|
||||
shouldCompile: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
prog, err := Compile(env, tt.expression)
|
||||
|
||||
if !tt.shouldCompile {
|
||||
if err == nil {
|
||||
t.Fatalf("%s: expected compilation to fail but it succeeded", tt.description)
|
||||
}
|
||||
return // Test passed - compilation failed as expected
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("failed to compile expression %q: %v", tt.expression, err)
|
||||
}
|
||||
|
||||
result, _, err := prog.Eval(tt.variables)
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewEnvironment(t *testing.T) {
|
||||
env, err := New()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create new environment: %v", err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
expression string
|
||||
variables map[string]interface{}
|
||||
expectBool *bool // nil if we just want to test compilation or non-bool result
|
||||
description string
|
||||
shouldCompile bool
|
||||
}{
|
||||
{
|
||||
name: "randInt-function-compilation",
|
||||
expression: `randInt(10)`,
|
||||
variables: map[string]interface{}{},
|
||||
expectBool: nil, // Don't check result, just compilation
|
||||
description: "should compile randInt function",
|
||||
shouldCompile: true,
|
||||
},
|
||||
{
|
||||
name: "randInt-range-validation",
|
||||
expression: `randInt(10) >= 0 && randInt(10) < 10`,
|
||||
variables: map[string]interface{}{},
|
||||
expectBool: boolPtr(true),
|
||||
description: "should return values in correct range",
|
||||
shouldCompile: true,
|
||||
},
|
||||
{
|
||||
name: "strings-extension-size",
|
||||
expression: `"hello".size() == 5`,
|
||||
variables: map[string]interface{}{},
|
||||
expectBool: boolPtr(true),
|
||||
description: "should support string extension functions",
|
||||
shouldCompile: true,
|
||||
},
|
||||
{
|
||||
name: "strings-extension-contains",
|
||||
expression: `"hello world".contains("world")`,
|
||||
variables: map[string]interface{}{},
|
||||
expectBool: boolPtr(true),
|
||||
description: "should support string contains function",
|
||||
shouldCompile: true,
|
||||
},
|
||||
{
|
||||
name: "strings-extension-startsWith",
|
||||
expression: `"hello world".startsWith("hello")`,
|
||||
variables: map[string]interface{}{},
|
||||
expectBool: boolPtr(true),
|
||||
description: "should support string startsWith function",
|
||||
shouldCompile: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
prog, err := Compile(env, tt.expression)
|
||||
|
||||
if !tt.shouldCompile {
|
||||
if err == nil {
|
||||
t.Fatalf("%s: expected compilation to fail but it succeeded", tt.description)
|
||||
}
|
||||
return // Test passed - compilation failed as expected
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("failed to compile expression %q: %v", tt.expression, err)
|
||||
}
|
||||
|
||||
// If we only want to test compilation, skip evaluation
|
||||
if tt.expectBool == nil {
|
||||
return
|
||||
}
|
||||
|
||||
result, _, err := prog.Eval(tt.variables)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to evaluate expression %q: %v", tt.expression, err)
|
||||
}
|
||||
|
||||
if result != types.Bool(*tt.expectBool) {
|
||||
t.Errorf("%s: expected %v, got %v", tt.description, *tt.expectBool, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to create bool pointers
|
||||
func boolPtr(b bool) *bool {
|
||||
return &b
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package expressions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/shirou/gopsutil/v4/load"
|
||||
)
|
||||
|
||||
type loadAvg struct {
|
||||
lock sync.RWMutex
|
||||
data *load.AvgStat
|
||||
}
|
||||
|
||||
func (l *loadAvg) updateThread(ctx context.Context) {
|
||||
ticker := time.NewTicker(15 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
l.update()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
l.update()
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *loadAvg) update() {
|
||||
l.lock.Lock()
|
||||
defer l.lock.Unlock()
|
||||
|
||||
var err error
|
||||
l.data, err = load.Avg()
|
||||
if err != nil {
|
||||
slog.Debug("can't get load average", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
globalLoadAvg *loadAvg
|
||||
)
|
||||
|
||||
func init() {
|
||||
globalLoadAvg = &loadAvg{}
|
||||
go globalLoadAvg.updateThread(context.Background())
|
||||
}
|
||||
|
||||
func Load1() float64 {
|
||||
globalLoadAvg.lock.RLock()
|
||||
defer globalLoadAvg.lock.RUnlock()
|
||||
return globalLoadAvg.data.Load1
|
||||
}
|
||||
|
||||
func Load5() float64 {
|
||||
globalLoadAvg.lock.RLock()
|
||||
defer globalLoadAvg.lock.RUnlock()
|
||||
return globalLoadAvg.data.Load5
|
||||
}
|
||||
|
||||
func Load15() float64 {
|
||||
globalLoadAvg.lock.RLock()
|
||||
defer globalLoadAvg.lock.RUnlock()
|
||||
return globalLoadAvg.data.Load15
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package bbolt
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
@@ -11,85 +12,52 @@ import (
|
||||
"go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
// Sentinel error values used for testing and in admin-visible error messages.
|
||||
var (
|
||||
ErrBucketDoesNotExist = errors.New("bbolt: bucket does not exist")
|
||||
ErrNotExists = errors.New("bbolt: value does not exist in store")
|
||||
)
|
||||
|
||||
// Store implements store.Interface backed by bbolt[1].
|
||||
//
|
||||
// In essence, bbolt is a hierarchical key/value store with a twist: every value
|
||||
// needs to belong to a bucket. Buckets can contain an infinite number of
|
||||
// buckets. As such, Anubis nests values in buckets. Each value in the store
|
||||
// is given its own bucket with two keys:
|
||||
//
|
||||
// 1. data - The raw data, usually in JSON
|
||||
// 2. expiry - The expiry time formatted as a time.RFC3339Nano timestamp string
|
||||
//
|
||||
// When Anubis stores a new bit of data, it creates a new bucket for that value.
|
||||
// This allows the cleanup phase to iterate over every bucket in the database and
|
||||
// only scan the expiry times without having to decode the entire record.
|
||||
//
|
||||
// bbolt is not suitable for environments where multiple instance of Anubis need
|
||||
// to read from and write to the same backend store. For that, use the valkey
|
||||
// storage backend.
|
||||
//
|
||||
// [1]: https://github.com/etcd-io/bbolt
|
||||
type Store struct {
|
||||
bdb *bbolt.DB
|
||||
type Item struct {
|
||||
Data []byte `json:"data"`
|
||||
Expires time.Time `json:"expires"`
|
||||
}
|
||||
|
||||
type Store struct {
|
||||
bucket []byte
|
||||
bdb *bbolt.DB
|
||||
}
|
||||
|
||||
// Delete a key from the datastore. If the key does not exist, return an error.
|
||||
func (s *Store) Delete(ctx context.Context, key string) error {
|
||||
return s.bdb.Update(func(tx *bbolt.Tx) error {
|
||||
if tx.Bucket([]byte(key)) == nil {
|
||||
bkt := tx.Bucket(s.bucket)
|
||||
if bkt == nil {
|
||||
return fmt.Errorf("%w: %q", ErrBucketDoesNotExist, string(s.bucket))
|
||||
}
|
||||
|
||||
if bkt.Get([]byte(key)) == nil {
|
||||
return fmt.Errorf("%w: %q", ErrNotExists, key)
|
||||
}
|
||||
|
||||
return tx.DeleteBucket([]byte(key))
|
||||
return bkt.Delete([]byte(key))
|
||||
})
|
||||
}
|
||||
|
||||
// Get a value from the datastore.
|
||||
//
|
||||
// Because each value is stored in its own bucket with data and expiry keys,
|
||||
// two get operations are required:
|
||||
//
|
||||
// 1. Get the expiry key, parse as time.RFC3339Nano. If the key has expired, run deletion in the background and return a "key not found" error.
|
||||
// 2. Get the data key, copy into the result byteslice, return it.
|
||||
func (s *Store) Get(ctx context.Context, key string) ([]byte, error) {
|
||||
var result []byte
|
||||
var i Item
|
||||
|
||||
if err := s.bdb.View(func(tx *bbolt.Tx) error {
|
||||
itemBucket := tx.Bucket([]byte(key))
|
||||
if itemBucket == nil {
|
||||
bkt := tx.Bucket(s.bucket)
|
||||
if bkt == nil {
|
||||
return fmt.Errorf("%w: %q", ErrBucketDoesNotExist, string(s.bucket))
|
||||
}
|
||||
|
||||
bucketData := bkt.Get([]byte(key))
|
||||
if bucketData == nil {
|
||||
return fmt.Errorf("%w: %q", store.ErrNotFound, key)
|
||||
}
|
||||
|
||||
expiryStr := itemBucket.Get([]byte("expiry"))
|
||||
if expiryStr == nil {
|
||||
return fmt.Errorf("[unexpected] %w: %q (expiry is nil)", store.ErrNotFound, key)
|
||||
}
|
||||
|
||||
expiry, err := time.Parse(time.RFC3339Nano, string(expiryStr))
|
||||
if err != nil {
|
||||
return fmt.Errorf("[unexpected] %w: %w", store.ErrCantDecode, err)
|
||||
}
|
||||
|
||||
if time.Now().After(expiry) {
|
||||
go s.Delete(context.Background(), key)
|
||||
return fmt.Errorf("%w: %q", store.ErrNotFound, key)
|
||||
}
|
||||
|
||||
dataStr := itemBucket.Get([]byte("data"))
|
||||
if dataStr == nil {
|
||||
return fmt.Errorf("[unexpected] %w: %q (data is nil)", store.ErrNotFound, key)
|
||||
}
|
||||
|
||||
result = make([]byte, len(dataStr))
|
||||
if n := copy(result, dataStr); n != len(dataStr) {
|
||||
return fmt.Errorf("[unexpected] %w: %d bytes copied of %d", store.ErrCantDecode, n, len(dataStr))
|
||||
if err := json.Unmarshal(bucketData, &i); err != nil {
|
||||
return fmt.Errorf("%w: %w", store.ErrCantDecode, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -97,28 +65,32 @@ func (s *Store) Get(ctx context.Context, key string) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
if time.Now().After(i.Expires) {
|
||||
go s.Delete(context.Background(), key)
|
||||
return nil, fmt.Errorf("%w: %q", store.ErrNotFound, key)
|
||||
}
|
||||
|
||||
return i.Data, nil
|
||||
}
|
||||
|
||||
// Set a value into the store with a given expiry.
|
||||
func (s *Store) Set(ctx context.Context, key string, value []byte, expiry time.Duration) error {
|
||||
expires := time.Now().Add(expiry)
|
||||
i := Item{
|
||||
Data: value,
|
||||
Expires: time.Now().Add(expiry),
|
||||
}
|
||||
|
||||
data, err := json.Marshal(i)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %w", store.ErrCantEncode, err)
|
||||
}
|
||||
|
||||
return s.bdb.Update(func(tx *bbolt.Tx) error {
|
||||
valueBkt, err := tx.CreateBucketIfNotExists([]byte(key))
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %w: %q (create bucket)", store.ErrCantEncode, err, key)
|
||||
bkt := tx.Bucket(s.bucket)
|
||||
if bkt == nil {
|
||||
return fmt.Errorf("%w: %q", ErrBucketDoesNotExist, string(s.bucket))
|
||||
}
|
||||
|
||||
if err := valueBkt.Put([]byte("expiry"), []byte(expires.Format(time.RFC3339Nano))); err != nil {
|
||||
return fmt.Errorf("%w: %q (expiry)", store.ErrCantEncode, key)
|
||||
}
|
||||
|
||||
if err := valueBkt.Put([]byte("data"), value); err != nil {
|
||||
return fmt.Errorf("%w: %q (data)", store.ErrCantEncode, key)
|
||||
}
|
||||
|
||||
return nil
|
||||
return bkt.Put([]byte(key), data)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -126,32 +98,35 @@ func (s *Store) cleanup(ctx context.Context) error {
|
||||
now := time.Now()
|
||||
|
||||
return s.bdb.Update(func(tx *bbolt.Tx) error {
|
||||
return tx.ForEach(func(key []byte, valueBkt *bbolt.Bucket) error {
|
||||
var expiry time.Time
|
||||
var err error
|
||||
bkt := tx.Bucket(s.bucket)
|
||||
if bkt == nil {
|
||||
return fmt.Errorf("cache bucket %q does not exist", string(s.bucket))
|
||||
}
|
||||
|
||||
expiryStr := valueBkt.Get([]byte("expiry"))
|
||||
if expiryStr == nil {
|
||||
slog.Warn("while running cleanup, expiry is not set somehow, file a bug?", "key", string(key))
|
||||
return nil
|
||||
return bkt.ForEach(func(k, v []byte) error {
|
||||
var i Item
|
||||
|
||||
data := bkt.Get(k)
|
||||
if data == nil {
|
||||
return fmt.Errorf("%s in Cache bucket does not exist???", string(k))
|
||||
}
|
||||
|
||||
expiry, err = time.Parse(time.RFC3339Nano, string(expiryStr))
|
||||
if err != nil {
|
||||
return fmt.Errorf("[unexpected] %w in bucket %q: %w", store.ErrCantDecode, string(key), err)
|
||||
if err := json.Unmarshal(data, &i); err != nil {
|
||||
return fmt.Errorf("can't unmarshal data at key %s: %w", string(k), err)
|
||||
}
|
||||
|
||||
if now.After(expiry) {
|
||||
return tx.DeleteBucket(key)
|
||||
if now.After(i.Expires) {
|
||||
return bkt.Delete(k)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (s *Store) cleanupThread(ctx context.Context) {
|
||||
t := time.NewTicker(time.Hour)
|
||||
t := time.NewTicker(5 * time.Minute)
|
||||
defer t.Stop()
|
||||
|
||||
for {
|
||||
|
||||
@@ -12,7 +12,8 @@ func TestImpl(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "db")
|
||||
t.Log(path)
|
||||
data, err := json.Marshal(Config{
|
||||
Path: path,
|
||||
Path: path,
|
||||
Bucket: "anubis",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -21,12 +21,8 @@ func init() {
|
||||
store.Register("bbolt", Factory{})
|
||||
}
|
||||
|
||||
// Factory builds new instances of the bbolt storage backend according to
|
||||
// configuration passed via a json.RawMessage.
|
||||
type Factory struct{}
|
||||
|
||||
// Build parses and validates the bbolt storage backend Config and creates
|
||||
// a new instance of it.
|
||||
func (Factory) Build(ctx context.Context, data json.RawMessage) (store.Interface, error) {
|
||||
var config Config
|
||||
if err := json.Unmarshal([]byte(data), &config); err != nil {
|
||||
@@ -37,13 +33,28 @@ func (Factory) Build(ctx context.Context, data json.RawMessage) (store.Interface
|
||||
return nil, fmt.Errorf("%w: %w", store.ErrBadConfig, err)
|
||||
}
|
||||
|
||||
if config.Bucket == "" {
|
||||
config.Bucket = "anubis"
|
||||
}
|
||||
|
||||
bdb, err := bbolt.Open(config.Path, 0600, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't open bbolt database %s: %w", config.Path, err)
|
||||
}
|
||||
|
||||
if err := bdb.Update(func(tx *bbolt.Tx) error {
|
||||
if _, err := tx.CreateBucketIfNotExists([]byte(config.Bucket)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("can't create bbolt bucket %q: %w", config.Bucket, err)
|
||||
}
|
||||
|
||||
result := &Store{
|
||||
bdb: bdb,
|
||||
bdb: bdb,
|
||||
bucket: []byte(config.Bucket),
|
||||
}
|
||||
|
||||
go result.cleanupThread(ctx)
|
||||
@@ -51,8 +62,6 @@ func (Factory) Build(ctx context.Context, data json.RawMessage) (store.Interface
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Valid parses and validates the bbolt store Config or returns
|
||||
// an error.
|
||||
func (Factory) Valid(data json.RawMessage) error {
|
||||
var config Config
|
||||
if err := json.Unmarshal([]byte(data), &config); err != nil {
|
||||
@@ -66,13 +75,11 @@ func (Factory) Valid(data json.RawMessage) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Config is the bbolt storage backend configuration.
|
||||
type Config struct {
|
||||
// Path is the filesystem path of the database. The folder must be writable to Anubis.
|
||||
Path string `json:"path"`
|
||||
Path string `json:"path"`
|
||||
Bucket string `json:"bucket,omitempty"`
|
||||
}
|
||||
|
||||
// Valid validates the configuration including checking if its containing folder is writable.
|
||||
func (c Config) Valid() error {
|
||||
var errs []error
|
||||
|
||||
@@ -83,7 +90,6 @@ func (c Config) Valid() error {
|
||||
if err := os.WriteFile(filepath.Join(dir, ".test-file"), []byte(""), 0600); err != nil {
|
||||
errs = append(errs, ErrCantWriteToPath)
|
||||
}
|
||||
os.Remove(filepath.Join(dir, ".test-file"))
|
||||
}
|
||||
|
||||
if len(errs) != 0 {
|
||||
|
||||
@@ -38,9 +38,7 @@ func Common(t *testing.T, f store.Factory, config json.RawMessage) {
|
||||
|
||||
val, err := s.Get(t.Context(), t.Name())
|
||||
if errors.Is(err, store.ErrNotFound) {
|
||||
t.Errorf("wanted %s to exist in store but it does not: %v", t.Name(), err)
|
||||
} else if err != nil {
|
||||
t.Error(err)
|
||||
t.Errorf("wanted %s to exist in store but it does not", t.Name())
|
||||
}
|
||||
|
||||
if !bytes.Equal(val, []byte(t.Name())) {
|
||||
|
||||
4
lib/testdata/permissive.yaml
vendored
4
lib/testdata/permissive.yaml
vendored
@@ -1,4 +0,0 @@
|
||||
bots:
|
||||
- import: (data)/common/allow-private-addresses.yaml
|
||||
|
||||
dnsbl: false
|
||||
421
package-lock.json
generated
421
package-lock.json
generated
@@ -1,17 +1,17 @@
|
||||
{
|
||||
"name": "@techaro/anubis",
|
||||
"version": "1.21.3",
|
||||
"version": "1.20.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@techaro/anubis",
|
||||
"version": "1.21.3",
|
||||
"version": "1.20.0",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"cssnano": "^7.1.0",
|
||||
"cssnano-preset-advanced": "^7.0.8",
|
||||
"esbuild": "^0.25.8",
|
||||
"cssnano": "^7.0.7",
|
||||
"cssnano-preset-advanced": "^7.0.7",
|
||||
"esbuild": "^0.25.5",
|
||||
"playwright": "^1.52.0",
|
||||
"postcss-cli": "^11.0.1",
|
||||
"postcss-import": "^16.1.1",
|
||||
@@ -20,9 +20,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz",
|
||||
"integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
|
||||
"integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -37,9 +37,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz",
|
||||
"integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz",
|
||||
"integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -54,9 +54,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz",
|
||||
"integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -71,9 +71,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz",
|
||||
"integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -88,9 +88,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz",
|
||||
"integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -105,9 +105,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz",
|
||||
"integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -122,9 +122,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz",
|
||||
"integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -139,9 +139,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz",
|
||||
"integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -156,9 +156,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz",
|
||||
"integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz",
|
||||
"integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -173,9 +173,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz",
|
||||
"integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -190,9 +190,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz",
|
||||
"integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz",
|
||||
"integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -207,9 +207,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz",
|
||||
"integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz",
|
||||
"integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
@@ -224,9 +224,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz",
|
||||
"integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz",
|
||||
"integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
@@ -241,9 +241,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz",
|
||||
"integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz",
|
||||
"integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -258,9 +258,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz",
|
||||
"integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz",
|
||||
"integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -275,9 +275,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz",
|
||||
"integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz",
|
||||
"integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -292,9 +292,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz",
|
||||
"integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -309,9 +309,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz",
|
||||
"integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -326,9 +326,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz",
|
||||
"integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -343,9 +343,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz",
|
||||
"integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -360,9 +360,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz",
|
||||
"integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -376,27 +376,10 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openharmony-arm64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz",
|
||||
"integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openharmony"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz",
|
||||
"integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -411,9 +394,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz",
|
||||
"integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -428,9 +411,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz",
|
||||
"integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz",
|
||||
"integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -445,9 +428,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz",
|
||||
"integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -461,6 +444,16 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@trysound/sax": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
|
||||
"integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
@@ -591,9 +584,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
"version": "4.25.1",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz",
|
||||
"integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==",
|
||||
"version": "4.25.0",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz",
|
||||
"integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -611,8 +604,8 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001726",
|
||||
"electron-to-chromium": "^1.5.173",
|
||||
"caniuse-lite": "^1.0.30001718",
|
||||
"electron-to-chromium": "^1.5.160",
|
||||
"node-releases": "^2.0.19",
|
||||
"update-browserslist-db": "^1.1.3"
|
||||
},
|
||||
@@ -637,9 +630,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001727",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz",
|
||||
"integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==",
|
||||
"version": "1.0.30001724",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001724.tgz",
|
||||
"integrity": "sha512-WqJo7p0TbHDOythNTqYujmaJTvtYRZrjpP8TCvH6Vb9CYJerJNKamKzIWOM4BkQatWj9H2lYulpdAQNBe7QhNA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -725,13 +718,13 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
|
||||
"integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==",
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
||||
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
@@ -755,9 +748,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/css-select": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz",
|
||||
"integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==",
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
|
||||
"integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
@@ -772,13 +765,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/css-tree": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz",
|
||||
"integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==",
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
|
||||
"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mdn-data": "2.12.2",
|
||||
"mdn-data": "2.0.30",
|
||||
"source-map-js": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -786,9 +779,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/css-what": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz",
|
||||
"integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==",
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
|
||||
"integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
@@ -812,13 +805,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/cssnano": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.1.0.tgz",
|
||||
"integrity": "sha512-Pu3rlKkd0ZtlCUzBrKL1Z4YmhKppjC1H9jo7u1o4qaKqyhvixFgu5qLyNIAOjSTg9DjVPtUqdROq2EfpVMEe+w==",
|
||||
"version": "7.0.7",
|
||||
"resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.0.7.tgz",
|
||||
"integrity": "sha512-evKu7yiDIF7oS+EIpwFlMF730ijRyLFaM2o5cTxRGJR9OKHKkc+qP443ZEVR9kZG0syaAJJCPJyfv5pbrxlSng==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cssnano-preset-default": "^7.0.8",
|
||||
"cssnano-preset-default": "^7.0.7",
|
||||
"lilconfig": "^3.1.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -833,15 +826,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/cssnano-preset-advanced": {
|
||||
"version": "7.0.8",
|
||||
"resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-7.0.8.tgz",
|
||||
"integrity": "sha512-KYw7gH8xmIzTwHefuM/m3lkMz4jn5EbjxZO2RHVsOGvrXCxSjbc0/f/gELWW9ZIgbQdJMCkijEo76gBYGY4S3Q==",
|
||||
"version": "7.0.7",
|
||||
"resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-7.0.7.tgz",
|
||||
"integrity": "sha512-uBLTct5OBy3r+WL8zB+RAUsok2E5eWdi3xvrotI0aS/E3KvEKKyaXIlAXLHUI0MSopHXJEuUX4jMTr9VkUtWkA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"autoprefixer": "^10.4.21",
|
||||
"browserslist": "^4.25.1",
|
||||
"cssnano-preset-default": "^7.0.8",
|
||||
"browserslist": "^4.24.5",
|
||||
"cssnano-preset-default": "^7.0.7",
|
||||
"postcss-discard-unused": "^7.0.4",
|
||||
"postcss-merge-idents": "^7.0.1",
|
||||
"postcss-reduce-idents": "^7.0.1",
|
||||
@@ -855,27 +848,27 @@
|
||||
}
|
||||
},
|
||||
"node_modules/cssnano-preset-default": {
|
||||
"version": "7.0.8",
|
||||
"resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.8.tgz",
|
||||
"integrity": "sha512-d+3R2qwrUV3g4LEMOjnndognKirBZISylDZAF/TPeCWVjEwlXS2e4eN4ICkoobRe7pD3H6lltinKVyS1AJhdjQ==",
|
||||
"version": "7.0.7",
|
||||
"resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.7.tgz",
|
||||
"integrity": "sha512-jW6CG/7PNB6MufOrlovs1TvBTEVmhY45yz+bd0h6nw3h6d+1e+/TX+0fflZ+LzvZombbT5f+KC063w9VoHeHow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"browserslist": "^4.25.1",
|
||||
"browserslist": "^4.24.5",
|
||||
"css-declaration-sorter": "^7.2.0",
|
||||
"cssnano-utils": "^5.0.1",
|
||||
"postcss-calc": "^10.1.1",
|
||||
"postcss-colormin": "^7.0.4",
|
||||
"postcss-convert-values": "^7.0.6",
|
||||
"postcss-colormin": "^7.0.3",
|
||||
"postcss-convert-values": "^7.0.5",
|
||||
"postcss-discard-comments": "^7.0.4",
|
||||
"postcss-discard-duplicates": "^7.0.2",
|
||||
"postcss-discard-empty": "^7.0.1",
|
||||
"postcss-discard-overridden": "^7.0.1",
|
||||
"postcss-merge-longhand": "^7.0.5",
|
||||
"postcss-merge-rules": "^7.0.6",
|
||||
"postcss-merge-rules": "^7.0.5",
|
||||
"postcss-minify-font-values": "^7.0.1",
|
||||
"postcss-minify-gradients": "^7.0.1",
|
||||
"postcss-minify-params": "^7.0.4",
|
||||
"postcss-minify-params": "^7.0.3",
|
||||
"postcss-minify-selectors": "^7.0.5",
|
||||
"postcss-normalize-charset": "^7.0.1",
|
||||
"postcss-normalize-display-values": "^7.0.1",
|
||||
@@ -883,13 +876,13 @@
|
||||
"postcss-normalize-repeat-style": "^7.0.1",
|
||||
"postcss-normalize-string": "^7.0.1",
|
||||
"postcss-normalize-timing-functions": "^7.0.1",
|
||||
"postcss-normalize-unicode": "^7.0.4",
|
||||
"postcss-normalize-unicode": "^7.0.3",
|
||||
"postcss-normalize-url": "^7.0.1",
|
||||
"postcss-normalize-whitespace": "^7.0.1",
|
||||
"postcss-ordered-values": "^7.0.2",
|
||||
"postcss-reduce-initial": "^7.0.4",
|
||||
"postcss-reduce-initial": "^7.0.3",
|
||||
"postcss-reduce-transforms": "^7.0.1",
|
||||
"postcss-svgo": "^7.1.0",
|
||||
"postcss-svgo": "^7.0.2",
|
||||
"postcss-unique-selectors": "^7.0.4"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1025,9 +1018,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.187",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.187.tgz",
|
||||
"integrity": "sha512-cl5Jc9I0KGUoOoSbxvTywTa40uspGJt/BDBoDLoxJRSBpWh4FFXBsjNRHfQrONsV/OoEjDfHUmZQa2d6Ze4YgA==",
|
||||
"version": "1.5.171",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.171.tgz",
|
||||
"integrity": "sha512-scWpzXEJEMrGJa4Y6m/tVotb0WuvNmasv3wWVzUAeCgKU0ToFOhUW6Z+xWnRQANMYGxN4ngJXIThgBJOqzVPCQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
@@ -1052,9 +1045,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz",
|
||||
"integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz",
|
||||
"integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
@@ -1065,32 +1058,31 @@
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.25.8",
|
||||
"@esbuild/android-arm": "0.25.8",
|
||||
"@esbuild/android-arm64": "0.25.8",
|
||||
"@esbuild/android-x64": "0.25.8",
|
||||
"@esbuild/darwin-arm64": "0.25.8",
|
||||
"@esbuild/darwin-x64": "0.25.8",
|
||||
"@esbuild/freebsd-arm64": "0.25.8",
|
||||
"@esbuild/freebsd-x64": "0.25.8",
|
||||
"@esbuild/linux-arm": "0.25.8",
|
||||
"@esbuild/linux-arm64": "0.25.8",
|
||||
"@esbuild/linux-ia32": "0.25.8",
|
||||
"@esbuild/linux-loong64": "0.25.8",
|
||||
"@esbuild/linux-mips64el": "0.25.8",
|
||||
"@esbuild/linux-ppc64": "0.25.8",
|
||||
"@esbuild/linux-riscv64": "0.25.8",
|
||||
"@esbuild/linux-s390x": "0.25.8",
|
||||
"@esbuild/linux-x64": "0.25.8",
|
||||
"@esbuild/netbsd-arm64": "0.25.8",
|
||||
"@esbuild/netbsd-x64": "0.25.8",
|
||||
"@esbuild/openbsd-arm64": "0.25.8",
|
||||
"@esbuild/openbsd-x64": "0.25.8",
|
||||
"@esbuild/openharmony-arm64": "0.25.8",
|
||||
"@esbuild/sunos-x64": "0.25.8",
|
||||
"@esbuild/win32-arm64": "0.25.8",
|
||||
"@esbuild/win32-ia32": "0.25.8",
|
||||
"@esbuild/win32-x64": "0.25.8"
|
||||
"@esbuild/aix-ppc64": "0.25.5",
|
||||
"@esbuild/android-arm": "0.25.5",
|
||||
"@esbuild/android-arm64": "0.25.5",
|
||||
"@esbuild/android-x64": "0.25.5",
|
||||
"@esbuild/darwin-arm64": "0.25.5",
|
||||
"@esbuild/darwin-x64": "0.25.5",
|
||||
"@esbuild/freebsd-arm64": "0.25.5",
|
||||
"@esbuild/freebsd-x64": "0.25.5",
|
||||
"@esbuild/linux-arm": "0.25.5",
|
||||
"@esbuild/linux-arm64": "0.25.5",
|
||||
"@esbuild/linux-ia32": "0.25.5",
|
||||
"@esbuild/linux-loong64": "0.25.5",
|
||||
"@esbuild/linux-mips64el": "0.25.5",
|
||||
"@esbuild/linux-ppc64": "0.25.5",
|
||||
"@esbuild/linux-riscv64": "0.25.5",
|
||||
"@esbuild/linux-s390x": "0.25.5",
|
||||
"@esbuild/linux-x64": "0.25.5",
|
||||
"@esbuild/netbsd-arm64": "0.25.5",
|
||||
"@esbuild/netbsd-x64": "0.25.5",
|
||||
"@esbuild/openbsd-arm64": "0.25.5",
|
||||
"@esbuild/openbsd-x64": "0.25.5",
|
||||
"@esbuild/sunos-x64": "0.25.5",
|
||||
"@esbuild/win32-arm64": "0.25.5",
|
||||
"@esbuild/win32-ia32": "0.25.5",
|
||||
"@esbuild/win32-x64": "0.25.5"
|
||||
}
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
@@ -1370,9 +1362,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mdn-data": {
|
||||
"version": "2.12.2",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz",
|
||||
"integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==",
|
||||
"version": "2.0.30",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
|
||||
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
|
||||
"dev": true,
|
||||
"license": "CC0-1.0"
|
||||
},
|
||||
@@ -1608,13 +1600,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-colormin": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.4.tgz",
|
||||
"integrity": "sha512-ziQuVzQZBROpKpfeDwmrG+Vvlr0YWmY/ZAk99XD+mGEBuEojoFekL41NCsdhyNUtZI7DPOoIWIR7vQQK9xwluw==",
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.3.tgz",
|
||||
"integrity": "sha512-xZxQcSyIVZbSsl1vjoqZAcMYYdnJsIyG8OvqShuuqf12S88qQboxxEy0ohNCOLwVPXTU+hFHvJPACRL2B5ohTA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"browserslist": "^4.25.1",
|
||||
"browserslist": "^4.24.5",
|
||||
"caniuse-api": "^3.0.0",
|
||||
"colord": "^2.9.3",
|
||||
"postcss-value-parser": "^4.2.0"
|
||||
@@ -1627,13 +1619,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-convert-values": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-7.0.6.tgz",
|
||||
"integrity": "sha512-MD/eb39Mr60hvgrqpXsgbiqluawYg/8K4nKsqRsuDX9f+xN1j6awZCUv/5tLH8ak3vYp/EMXwdcnXvfZYiejCQ==",
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-7.0.5.tgz",
|
||||
"integrity": "sha512-0VFhH8nElpIs3uXKnVtotDJJNX0OGYSZmdt4XfSfvOMrFw1jKfpwpZxfC4iN73CTM/MWakDEmsHQXkISYj4BXw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"browserslist": "^4.25.1",
|
||||
"browserslist": "^4.24.5",
|
||||
"postcss-value-parser": "^4.2.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1827,13 +1819,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-merge-rules": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-7.0.6.tgz",
|
||||
"integrity": "sha512-2jIPT4Tzs8K87tvgCpSukRQ2jjd+hH6Bb8rEEOUDmmhOeTcqDg5fEFK8uKIu+Pvc3//sm3Uu6FRqfyv7YF7+BQ==",
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-7.0.5.tgz",
|
||||
"integrity": "sha512-ZonhuSwEaWA3+xYbOdJoEReKIBs5eDiBVLAGpYZpNFPzXZcEE5VKR7/qBEQvTZpiwjqhhqEQ+ax5O3VShBj9Wg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"browserslist": "^4.25.1",
|
||||
"browserslist": "^4.24.5",
|
||||
"caniuse-api": "^3.0.0",
|
||||
"cssnano-utils": "^5.0.1",
|
||||
"postcss-selector-parser": "^7.1.0"
|
||||
@@ -1880,13 +1872,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-minify-params": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-7.0.4.tgz",
|
||||
"integrity": "sha512-3OqqUddfH8c2e7M35W6zIwv7jssM/3miF9cbCSb1iJiWvtguQjlxZGIHK9JRmc8XAKmE2PFGtHSM7g/VcW97sw==",
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-7.0.3.tgz",
|
||||
"integrity": "sha512-vUKV2+f5mtjewYieanLX0xemxIp1t0W0H/D11u+kQV/MWdygOO7xPMkbK+r9P6Lhms8MgzKARF/g5OPXhb8tgg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"browserslist": "^4.25.1",
|
||||
"browserslist": "^4.24.5",
|
||||
"cssnano-utils": "^5.0.1",
|
||||
"postcss-value-parser": "^4.2.0"
|
||||
},
|
||||
@@ -2008,13 +2000,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-normalize-unicode": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.4.tgz",
|
||||
"integrity": "sha512-LvIURTi1sQoZqj8mEIE8R15yvM+OhbR1avynMtI9bUzj5gGKR/gfZFd8O7VMj0QgJaIFzxDwxGl/ASMYAkqO8g==",
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.3.tgz",
|
||||
"integrity": "sha512-EcoA29LvG3F+EpOh03iqu+tJY3uYYKzArqKJHxDhUYLa2u58aqGq16K6/AOsXD9yqLN8O6y9mmePKN5cx6krOw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"browserslist": "^4.25.1",
|
||||
"browserslist": "^4.24.5",
|
||||
"postcss-value-parser": "^4.2.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -2090,13 +2082,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-reduce-initial": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-7.0.4.tgz",
|
||||
"integrity": "sha512-rdIC9IlMBn7zJo6puim58Xd++0HdbvHeHaPgXsimMfG1ijC5A9ULvNLSE0rUKVJOvNMcwewW4Ga21ngyJjY/+Q==",
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-7.0.3.tgz",
|
||||
"integrity": "sha512-RFvkZaqiWtGMlVjlUHpaxGqEL27lgt+Q2Ixjf83CRAzqdo+TsDyGPtJUbPx2MuYIJ+sCQc2TrOvRnhcXQfgIVA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"browserslist": "^4.25.1",
|
||||
"browserslist": "^4.24.5",
|
||||
"caniuse-api": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -2164,14 +2156,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-svgo": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-7.1.0.tgz",
|
||||
"integrity": "sha512-KnAlfmhtoLz6IuU3Sij2ycusNs4jPW+QoFE5kuuUOK8awR6tMxZQrs5Ey3BUz7nFCzT3eqyFgqkyrHiaU2xx3w==",
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-7.0.2.tgz",
|
||||
"integrity": "sha512-5Dzy66JlnRM6pkdOTF8+cGsB1fnERTE8Nc+Eed++fOWo1hdsBptCsbG8UuJkgtZt75bRtMJIrPeZmtfANixdFA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"postcss-value-parser": "^4.2.0",
|
||||
"svgo": "^4.0.0"
|
||||
"svgo": "^3.3.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.12.0 || ^20.9.0 || >= 18"
|
||||
@@ -2326,13 +2318,6 @@
|
||||
"url": "0.10.x"
|
||||
}
|
||||
},
|
||||
"node_modules/sax": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
|
||||
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
@@ -2425,25 +2410,25 @@
|
||||
}
|
||||
},
|
||||
"node_modules/svgo": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.0.tgz",
|
||||
"integrity": "sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw==",
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz",
|
||||
"integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"commander": "^11.1.0",
|
||||
"@trysound/sax": "0.2.0",
|
||||
"commander": "^7.2.0",
|
||||
"css-select": "^5.1.0",
|
||||
"css-tree": "^3.0.1",
|
||||
"css-tree": "^2.3.1",
|
||||
"css-what": "^6.1.0",
|
||||
"csso": "^5.0.5",
|
||||
"picocolors": "^1.1.1",
|
||||
"sax": "^1.4.1"
|
||||
"picocolors": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"svgo": "bin/svgo.js"
|
||||
"svgo": "bin/svgo"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@techaro/anubis",
|
||||
"version": "1.21.3",
|
||||
"version": "1.20.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
@@ -18,9 +18,9 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"cssnano": "^7.1.0",
|
||||
"cssnano-preset-advanced": "^7.0.8",
|
||||
"esbuild": "^0.25.8",
|
||||
"cssnano": "^7.0.7",
|
||||
"cssnano-preset-advanced": "^7.0.7",
|
||||
"esbuild": "^0.25.5",
|
||||
"playwright": "^1.52.0",
|
||||
"postcss-cli": "^11.0.1",
|
||||
"postcss-import": "^16.1.1",
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
services:
|
||||
cgit:
|
||||
image: joseluisq/alpine-cgit
|
||||
pull_policy: always
|
||||
restart: always
|
||||
environment:
|
||||
CGIT_TITLE: Test git server
|
||||
CGIT_DESC: Test server, please ignore
|
||||
volumes:
|
||||
- ./var/repos:/srv/git
|
||||
|
||||
anubis:
|
||||
image: ko.local/anubis
|
||||
environment:
|
||||
BIND: ":8005"
|
||||
TARGET: http://cgit:80
|
||||
USE_REMOTE_ADDRESS: "true"
|
||||
ports:
|
||||
- 8005:8005
|
||||
|
||||
volumes:
|
||||
cgit-data:
|
||||
@@ -1,26 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
export VERSION=$GITHUB_COMMIT-test
|
||||
export KO_DOCKER_REPO=ko.local
|
||||
|
||||
set -u
|
||||
|
||||
(
|
||||
cd ../.. && \
|
||||
ko build --platform=all --base-import-paths --tags="latest" --image-user=1000 --image-annotation="" --image-label="" ./cmd/anubis -L
|
||||
)
|
||||
|
||||
rm -rf ./var/repos ./var/clones
|
||||
mkdir -p ./var/repos ./var/clones
|
||||
|
||||
(cd ./var/repos && git clone --bare https://github.com/TecharoHQ/status.git)
|
||||
|
||||
docker compose up -d
|
||||
|
||||
sleep 2
|
||||
|
||||
(cd ./var/clones && git clone http://localhost:8005/status.git)
|
||||
|
||||
docker compose down
|
||||
2
test/git-clone/var/.gitignore
vendored
2
test/git-clone/var/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -1,16 +0,0 @@
|
||||
services:
|
||||
git:
|
||||
image: ghcr.io/kutespaces/simple-git-http-server
|
||||
pull_policy: always
|
||||
restart: always
|
||||
volumes:
|
||||
- ./var/repos:/git
|
||||
|
||||
anubis:
|
||||
image: ko.local/anubis
|
||||
environment:
|
||||
BIND: ":3000"
|
||||
TARGET: http://git:80
|
||||
USE_REMOTE_ADDRESS: "true"
|
||||
ports:
|
||||
- 3000:3000
|
||||
@@ -1,37 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
export VERSION=$GITHUB_COMMIT-test
|
||||
export KO_DOCKER_REPO=ko.local
|
||||
|
||||
set -u
|
||||
|
||||
(
|
||||
cd ../.. && \
|
||||
ko build --platform=all --base-import-paths --tags="latest" --image-user=1000 --image-annotation="" --image-label="" ./cmd/anubis -L
|
||||
)
|
||||
|
||||
rm -rf ./var/repos ./var/foo
|
||||
mkdir -p ./var/repos
|
||||
|
||||
(cd ./var/repos && git init --bare foo.git && cd foo.git && git config http.receivepack true)
|
||||
|
||||
docker compose up -d
|
||||
|
||||
sleep 2
|
||||
|
||||
(
|
||||
cd var && \
|
||||
mkdir foo && \
|
||||
cd foo && \
|
||||
git init && \
|
||||
touch README && \
|
||||
git add . && \
|
||||
git config user.name "Anubis CI" && \
|
||||
git config user.email "social+anubis-ci@techaro.lol" && \
|
||||
git commit -sm "initial commit" && \
|
||||
git push -u http://localhost:3000/git/foo.git master
|
||||
)
|
||||
|
||||
docker compose down
|
||||
2
test/git-push/var/.gitignore
vendored
2
test/git-push/var/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
*
|
||||
!.gitignore
|
||||
29
test/go.mod
29
test/go.mod
@@ -14,16 +14,14 @@ require (
|
||||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250425153114-8976f5be98c1.1 // indirect
|
||||
cel.dev/expr v0.24.0 // indirect
|
||||
github.com/TecharoHQ/thoth-proto v0.4.0 // indirect
|
||||
github.com/a-h/templ v0.3.906 // indirect
|
||||
github.com/a-h/templ v0.3.898 // indirect
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/ebitengine/purego v0.8.4 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c // indirect
|
||||
github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 // indirect
|
||||
github.com/gaissmai/bart v0.20.5 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/gaissmai/bart v0.20.4 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||
github.com/google/cel-go v0.25.0 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 // indirect
|
||||
@@ -31,32 +29,25 @@ require (
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
github.com/jsha/minica v1.1.0 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/lum8rjack/go-ja4h v0.0.0-20250606032308-3a989c6635be // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/prometheus/client_golang v1.22.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.64.0 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/redis/go-redis/v9 v9.11.0 // indirect
|
||||
github.com/sebest/xff v0.0.0-20210106013422-671bd2870b3a // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.25.6 // indirect
|
||||
github.com/stoewer/go-strcase v1.3.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.etcd.io/bbolt v1.4.2 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
github.com/yl2chen/cidranger v1.0.2 // indirect
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
|
||||
golang.org/x/net v0.42.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect
|
||||
google.golang.org/grpc v1.73.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
k8s.io/apimachinery v0.33.2 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||
sigs.k8s.io/yaml v1.5.0 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
)
|
||||
|
||||
tool (
|
||||
|
||||
172
test/go.sum
172
test/go.sum
@@ -2,53 +2,21 @@ buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-2025042515311
|
||||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250425153114-8976f5be98c1.1/go.mod h1:avRlCjnFzl98VPaeCtJ24RrV/wwHFzB8sWXhj26+n/U=
|
||||
cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
|
||||
cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
|
||||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/TecharoHQ/thoth-proto v0.4.0 h1:UbkvfgCku0Dm1R6O4ug3HOsJNnE6F3wB8x+Dpw2lzFI=
|
||||
github.com/TecharoHQ/thoth-proto v0.4.0/go.mod h1:IcGnZt3iYUZQVEa0Lwk5l4ix0hCeXlWUV1TJMZvbWx0=
|
||||
github.com/a-h/templ v0.3.906 h1:ZUThc8Q9n04UATaCwaG60pB1AqbulLmYEAMnWV63svg=
|
||||
github.com/a-h/templ v0.3.906/go.mod h1:FFAu4dI//ESmEN7PQkJ7E7QfnSEMdcnu7QrAY8Dn334=
|
||||
github.com/a-h/templ v0.3.898 h1:g9oxL/dmM6tvwRe2egJS8hBDQTncokbMoOFk1oJMX7s=
|
||||
github.com/a-h/templ v0.3.898/go.mod h1:oLBbZVQ6//Q6zpvSMPTuBK0F3qOtBdFBcGRspcT+VNQ=
|
||||
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/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
|
||||
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
|
||||
github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA=
|
||||
github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/docker v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0=
|
||||
github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
|
||||
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0=
|
||||
github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
|
||||
github.com/facebookgo/flagenv v0.0.0-20160425205200-fcd59fca7456 h1:CkmB2l68uhvRlwOTPrwnuitSxi/S3Cg4L5QYOcL9MBc=
|
||||
@@ -57,31 +25,28 @@ github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojt
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
|
||||
github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk=
|
||||
github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/gaissmai/bart v0.20.5 h1:ehoWZWQ7j//qt0K0Zs4i9hpoPpbgqsMQiR8W2QPJh+c=
|
||||
github.com/gaissmai/bart v0.20.5/go.mod h1:cEed+ge8dalcbpi8wtS9x9m2hn/fNJH5suhdGQOHnYk=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/gaissmai/bart v0.20.4 h1:Ik47r1fy3jRVU+1eYzKSW3ho2UgBVTVnUS8O993584U=
|
||||
github.com/gaissmai/bart v0.20.4/go.mod h1:cEed+ge8dalcbpi8wtS9x9m2hn/fNJH5suhdGQOHnYk=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/cel-go v0.25.0 h1:jsFw9Fhn+3y2kBbltZR4VEz5xKkcIFRPDnuEzAGv5GY=
|
||||
github.com/google/cel-go v0.25.0/go.mod h1:hjEb6r5SuOSlhCHmFoLzu8HGCERvIsDAbxDAyNU/MmI=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
@@ -95,41 +60,10 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/lum8rjack/go-ja4h v0.0.0-20250606032308-3a989c6635be h1:dVIND0nXGXPQnFZYrMXT6CxHhBYhTPMm0GFqcmfaIC4=
|
||||
github.com/lum8rjack/go-ja4h v0.0.0-20250606032308-3a989c6635be/go.mod h1:q68TUR45WDa2r3yU4aO6WgxfCc0Vj1qtRaKaRE3yMLM=
|
||||
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
|
||||
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
|
||||
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
|
||||
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
||||
github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg=
|
||||
github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU=
|
||||
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
|
||||
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.0 h1:C/m2NNWNiTB6SK4Ao8df5EWm3JETSTIGNXBpMJTxzxQ=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.0/go.mod h1:88sRqr0C6OPyJn0/KRNaEz1uWorjxIKP7rUUcvycecE=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
@@ -138,85 +72,63 @@ github.com/prometheus/common v0.64.0 h1:pdZeA+g617P7oGv1CzdTzyeShxAGrTBsolKNOLQP
|
||||
github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
|
||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||
github.com/redis/go-redis/v9 v9.11.0 h1:E3S08Gl/nJNn5vkxd2i78wZxWAPNZgUNTp8WIJUAiIs=
|
||||
github.com/redis/go-redis/v9 v9.11.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
|
||||
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.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs=
|
||||
github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
|
||||
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/testcontainers/testcontainers-go v0.37.0 h1:L2Qc0vkTw2EHWQ08djon0D2uw7Z/PtHS/QzZZ5Ra/hg=
|
||||
github.com/testcontainers/testcontainers-go v0.37.0/go.mod h1:QPzbxZhQ6Bclip9igjLFj6z0hs01bU8lrl2dHQmgFGM=
|
||||
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.etcd.io/bbolt v1.4.2 h1:IrUHp260R8c+zYx/Tm8QZr04CX+qWS5PGfPdevhdm1I=
|
||||
go.etcd.io/bbolt v1.4.2/go.mod h1:Is8rSHO/b4f3XigBC0lL0+4FwAQv3HXEEIgFMuKHceM=
|
||||
github.com/yl2chen/cidranger v1.0.2 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+SevawU=
|
||||
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
|
||||
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.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
|
||||
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/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.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
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.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||
go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE=
|
||||
go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI=
|
||||
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
|
||||
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
|
||||
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
|
||||
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
|
||||
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
|
||||
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
|
||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a h1:SGktgSolFCo75dnHJF2yMvnns6jCmHFJ0vE4Vn2JKvQ=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a/go.mod h1:a77HrdMjoeKbnd2jmgcWdaS++ZLZAEq3orIOAEIKiVw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8=
|
||||
google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/apimachinery v0.33.2 h1:IHFVhqg59mb8PJWTLi8m1mAoepkUNYmptHsV+Z1m5jY=
|
||||
k8s.io/apimachinery v0.33.1 h1:mzqXWV8tW9Rw4VeW9rEkqvnxj59k1ezDUl20tFK/oM4=
|
||||
k8s.io/apimachinery v0.33.1/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
||||
k8s.io/apimachinery v0.33.2/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
|
||||
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
services:
|
||||
web:
|
||||
image: ghcr.io/xe/nginx-micro:v1.29.0
|
||||
|
||||
anubis:
|
||||
image: ko.local/anubis
|
||||
environment:
|
||||
TARGET: http://web:80
|
||||
USE_REMOTE_ADDRESS: "true"
|
||||
healthcheck:
|
||||
test: ["CMD", "anubis", "--healthcheck"]
|
||||
interval: 5s
|
||||
timeout: 30s
|
||||
retries: 5
|
||||
start_period: 500ms
|
||||
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
export VERSION=$GITHUB_COMMIT-test
|
||||
export KO_DOCKER_REPO=ko.local
|
||||
|
||||
set -u
|
||||
|
||||
(
|
||||
cd ../.. && \
|
||||
ko build --platform=all --base-import-paths --tags="latest" --image-user=1000 --image-annotation="" --image-label="" ./cmd/anubis -L
|
||||
)
|
||||
|
||||
docker compose up -d
|
||||
|
||||
attempt=1
|
||||
max_attempts=5
|
||||
delay=2
|
||||
|
||||
while ! docker compose ps | grep healthy; do
|
||||
if (( attempt >= max_attempts )); then
|
||||
echo "Service did not become healthy after $max_attempts attempts."
|
||||
exit 1
|
||||
fi
|
||||
echo "Waiting for healthy service... attempt $attempt"
|
||||
sleep $delay
|
||||
delay=$(( delay * 2 ))
|
||||
attempt=$(( attempt + 1 ))
|
||||
done
|
||||
|
||||
docker compose down
|
||||
2
test/healthcheck/var/.gitignore
vendored
2
test/healthcheck/var/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -1,8 +0,0 @@
|
||||
bots:
|
||||
- name: challenge
|
||||
user_agent_regex: CHALLENGE
|
||||
action: CHALLENGE
|
||||
|
||||
status_codes:
|
||||
CHALLENGE: 200
|
||||
DENY: 403
|
||||
@@ -1,62 +0,0 @@
|
||||
async function fetchLanguages() {
|
||||
return fetch("http://localhost:8923/.within.website/x/cmd/anubis/static/locales/manifest.json")
|
||||
.then(resp => {
|
||||
if (resp.status !== 200) {
|
||||
throw new Error(`wanted status 200, got status: ${resp.status}`);
|
||||
}
|
||||
return resp;
|
||||
})
|
||||
.then(resp => resp.json());
|
||||
}
|
||||
|
||||
async function getChallengePage(lang) {
|
||||
return fetch("http://localhost:8923/reqmeta", {
|
||||
headers: {
|
||||
"Accept-Language": lang,
|
||||
"User-Agent": "CHALLENGE",
|
||||
}
|
||||
})
|
||||
.then(resp => {
|
||||
if (resp.status !== 200) {
|
||||
throw new Error(`wanted status 200, got status: ${resp.status}`);
|
||||
}
|
||||
return resp;
|
||||
})
|
||||
.then(resp => resp.text());
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const languages = await fetchLanguages();
|
||||
console.log(languages);
|
||||
|
||||
const { supportedLanguages } = languages;
|
||||
|
||||
if (supportedLanguages.length === 0) {
|
||||
throw new Error(`no languages defined`);
|
||||
}
|
||||
|
||||
const resultSheet = {};
|
||||
let failed = false;
|
||||
|
||||
for (const lang of supportedLanguages) {
|
||||
console.log(`getting for ${lang}`);
|
||||
const page = await getChallengePage(lang);
|
||||
|
||||
resultSheet[lang] = page.includes(`<html lang="${lang}">`)
|
||||
}
|
||||
|
||||
for (const [lang, result] of Object.entries(resultSheet)) {
|
||||
if (!result) {
|
||||
failed = true;
|
||||
console.log(`${lang} did not show up in challenge page`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(resultSheet);
|
||||
|
||||
if (failed) {
|
||||
throw new Error("i18n smoke test failed");
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
})();
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
function cleanup() {
|
||||
pkill -P $$
|
||||
}
|
||||
|
||||
trap cleanup EXIT SIGINT
|
||||
|
||||
# Build static assets
|
||||
(cd ../.. && npm ci && npm run assets)
|
||||
|
||||
go tool anubis --help 2>/dev/null ||:
|
||||
|
||||
go run ../cmd/unixhttpd &
|
||||
|
||||
go tool anubis \
|
||||
--policy-fname ./anubis.yaml \
|
||||
--use-remote-address \
|
||||
--target=unix://$(pwd)/unixhttpd.sock &
|
||||
|
||||
backoff-retry node ./test.mjs
|
||||
@@ -1,42 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
startWait = flag.Duration("start-wait", 250*time.Millisecond, "amount of time to start with exponential backoff")
|
||||
tryCount = flag.Int("try-count", 5, "number of retries")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
cmdStr := strings.Join(flag.Args(), " ")
|
||||
wait := *startWait
|
||||
|
||||
for i := range make([]struct{}, *tryCount) {
|
||||
slog.Info("executing", "try", i+1, "wait", wait, "cmd", cmdStr)
|
||||
|
||||
cmd := exec.Command("sh", "-c", cmdStr)
|
||||
cmd.Stdin = nil
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
time.Sleep(wait)
|
||||
wait = wait * 2
|
||||
} else {
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("giving up after %d tries\n", *tryCount)
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -25,7 +25,7 @@ func Index(localizer *localization.SimpleLocalizer) templ.Component {
|
||||
return index(localizer)
|
||||
}
|
||||
|
||||
func ErrorPage(msg, mail string, localizer *localization.SimpleLocalizer) templ.Component {
|
||||
func ErrorPage(msg string, mail string, localizer *localization.SimpleLocalizer) templ.Component {
|
||||
return errorPage(msg, mail, localizer)
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user