813 Commits

Author SHA1 Message Date
dependabot[bot] c3474d5bfb build(deps-dev): bump prettier from 3.8.5 to 3.9.1 in the npm group (#1729)
Co-authored-by: Jason Cameron <git@jasoncameron.dev>
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Jason Cameron <git@jasoncameron.dev>
2026-07-09 03:18:03 +00:00
dependabot[bot] fcc940de03 build(deps): bump the gomod group with 2 updates (#1730)
Co-authored-by: Jason Cameron <git@jasoncameron.dev>
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Jason Cameron <git@jasoncameron.dev>
2026-07-09 03:08:35 +00:00
dependabot[bot] 786028998d build(deps): bump the github-actions group across 1 directory with 14 updates (#1725)
Co-authored-by: Jason Cameron <git@jasoncameron.dev>
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Jason Cameron <git@jasoncameron.dev>
2026-07-05 00:44:27 -04:00
dependabot[bot] 377a3200ca build(deps): bump the gomod group across 1 directory with 16 updates (#1727)
Co-authored-by: Jason Cameron <git@jasoncameron.dev>
Signed-off-by: dependabot[bot] <support@github.com>
2026-07-04 23:22:17 -04:00
Jason Cameron b7b4c1a914 chore: update playwright dependencies (#1279)
Signed-off-by: Jason Cameron <jason.cameron@stanwith.me>
2026-07-05 01:53:06 +00:00
Bruno Bernard 9a9411eb65 docs: add anubis-kubernetes-operator as another option (#1675)
Signed-off-by: Bruno Bernard <contact.brunobernard@gmail.com>
Signed-off-by: Xe Iaso <xe.iaso@techaro.lol>
Co-authored-by: Xe Iaso <xe.iaso@techaro.lol>
2026-07-04 00:25:58 -04:00
dependabot[bot] 17e9fc9544 build(deps): bump the npm group across 1 directory with 8 updates (#1723)
Bumps the npm group with 7 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [preact](https://github.com/preactjs/preact) | `10.29.2` | `10.29.3` |
| [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli) | `21.0.2` | `21.1.0` |
| [baseline-browser-mapping](https://github.com/web-platform-dx/baseline-browser-mapping) | `2.10.34` | `2.10.40` |
| [cssnano](https://github.com/cssnano/cssnano) | `8.0.1` | `8.0.2` |
| [cssnano-preset-advanced](https://github.com/cssnano/cssnano) | `8.0.1` | `8.0.2` |
| [esbuild](https://github.com/evanw/esbuild) | `0.28.0` | `0.28.1` |
| [prettier](https://github.com/prettier/prettier) | `3.8.3` | `3.8.5` |



Updates `preact` from 10.29.2 to 10.29.3
- [Release notes](https://github.com/preactjs/preact/releases)
- [Commits](https://github.com/preactjs/preact/compare/10.29.2...10.29.3)

Updates `@commitlint/cli` from 21.0.2 to 21.1.0
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/cli/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v21.1.0/@commitlint/cli)

Updates `@commitlint/config-conventional` from 21.0.2 to 21.2.0
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v21.2.0/@commitlint/config-conventional)

Updates `baseline-browser-mapping` from 2.10.34 to 2.10.40
- [Release notes](https://github.com/web-platform-dx/baseline-browser-mapping/releases)
- [Commits](https://github.com/web-platform-dx/baseline-browser-mapping/compare/v2.10.34...v2.10.40)

Updates `cssnano` from 8.0.1 to 8.0.2
- [Release notes](https://github.com/cssnano/cssnano/releases)
- [Commits](https://github.com/cssnano/cssnano/compare/cssnano@8.0.1...cssnano@8.0.2)

Updates `cssnano-preset-advanced` from 8.0.1 to 8.0.2
- [Release notes](https://github.com/cssnano/cssnano/releases)
- [Commits](https://github.com/cssnano/cssnano/compare/cssnano-preset-advanced@8.0.1...cssnano-preset-advanced@8.0.2)

Updates `esbuild` from 0.28.0 to 0.28.1
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.28.0...v0.28.1)

Updates `prettier` from 3.8.3 to 3.8.5
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.8.3...3.8.5)

---
updated-dependencies:
- dependency-name: preact
  dependency-version: 10.29.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: "@commitlint/cli"
  dependency-version: 21.1.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: "@commitlint/config-conventional"
  dependency-version: 21.2.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: baseline-browser-mapping
  dependency-version: 2.10.40
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: cssnano
  dependency-version: 8.0.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: cssnano-preset-advanced
  dependency-version: 8.0.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: esbuild
  dependency-version: 0.28.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: prettier
  dependency-version: 3.8.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-03 15:42:43 -04:00
Xe Iaso a3b6c555d0 chore: add golangci words to the spelling dictionary (#1724)
Signed-off-by: Xe Iaso <me@xeiaso.net>
2026-07-03 15:35:18 -04:00
Xe Iaso 2673b2de16 chore: set up golangci-lint (#1720)
* chore: add golangci-lint config

Signed-off-by: Xe Iaso <me@xeiaso.net>

* chore: mechanically fix lint issues

Signed-off-by: Xe Iaso <me@xeiaso.net>

* ci: add golangci-lint CI step

Signed-off-by: Xe Iaso <me@xeiaso.net>

* ci(golangci): build assets because linters need that for some reason

Signed-off-by: Xe Iaso <me@xeiaso.net>

* ci(golangci): rename job to avoid name conflicts

Signed-off-by: Xe Iaso <me@xeiaso.net>

* chore: ignore node_modules in golangci-lint

Signed-off-by: Xe Iaso <me@xeiaso.net>

---------

Signed-off-by: Xe Iaso <me@xeiaso.net>
2026-07-03 15:34:08 -04:00
Xe Iaso 7b93469576 ci: add cspell to replace check-spelling (#1722)
* ci: add cspell to replace check-spelling

Signed-off-by: Xe Iaso <me@xeiaso.net>

* chore: update known words in spellcheck list

Signed-off-by: Xe Iaso <me@xeiaso.net>

---------

Signed-off-by: Xe Iaso <me@xeiaso.net>
2026-07-03 15:21:04 -04:00
mhardy-qwant a1a5fbd098 feat(qwantbot): update remote addresses (#1719)
Signed-off-by: Mathieu Hardy <m.hardy@qwant.com>
2026-07-02 16:42:44 -04:00
Xe Iaso ce7acf098b fix(yeetfile): quoting is a sin
Signed-off-by: Xe Iaso <me@xeiaso.net>
v1.26.0-pre1
2026-06-25 23:52:57 -04:00
Xe Iaso 13c05eef7a chore: downgrade go.mod version to 1.26.3
Signed-off-by: Xe Iaso <me@xeiaso.net>
2026-06-25 23:03:18 -04:00
Xe Iaso 679b447fb1 chore: tag v1.26.0-pre1 (#1705)
Signed-off-by: Xe Iaso <me@xeiaso.net>
2026-06-25 23:00:05 -04:00
Xe Iaso d19b86c6e1 chore: bump go version to 1.26 (#1695)
* chore: bump go version to 1.26

Signed-off-by: Xe Iaso <me@xeiaso.net>

* ci(asset-verification): bump go

Signed-off-by: Xe Iaso <me@xeiaso.net>

* chore: fix??

Signed-off-by: Xe Iaso <me@xeiaso.net>

* fix(main): use ReverseProxy.Rewrite instead of Director (deprecated)

Signed-off-by: Xe Iaso <me@xeiaso.net>

---------

Signed-off-by: Xe Iaso <me@xeiaso.net>
2026-06-18 22:43:44 -04:00
Xe Iaso 3bd30e4f63 chore(test): bump to go 1.26
Signed-off-by: Xe Iaso <me@xeiaso.net>
2026-06-18 21:37:52 -04:00
Jake Howard 40d497332e docs: use stable container in traefik deployment example (#1676)
"main" is the most recent commit, whilst "latest" is the most recent
release.

Signed-off-by: Jake Howard <git@theorangeone.net>
2026-06-17 23:13:26 +00:00
dependabot[bot] 13ec01a847 build(deps-dev): bump the npm group across 1 directory with 4 updates (#1677)
Bumps the npm group with 4 updates in the / directory: [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli), [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/config-conventional), [baseline-browser-mapping](https://github.com/web-platform-dx/baseline-browser-mapping) and [postcss-url](https://github.com/postcss/postcss-url).


Updates `@commitlint/cli` from 21.0.1 to 21.0.2
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/cli/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v21.0.2/@commitlint/cli)

Updates `@commitlint/config-conventional` from 21.0.1 to 21.0.2
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v21.0.2/@commitlint/config-conventional)

Updates `baseline-browser-mapping` from 2.10.30 to 2.10.34
- [Release notes](https://github.com/web-platform-dx/baseline-browser-mapping/releases)
- [Commits](https://github.com/web-platform-dx/baseline-browser-mapping/compare/v2.10.30...v2.10.34)

Updates `postcss-url` from 10.1.3 to 10.1.4
- [Release notes](https://github.com/postcss/postcss-url/releases)
- [Changelog](https://github.com/postcss/postcss-url/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss-url/compare/10.1.3...10.1.4)

---
updated-dependencies:
- dependency-name: "@commitlint/cli"
  dependency-version: 21.0.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: "@commitlint/config-conventional"
  dependency-version: 21.0.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: baseline-browser-mapping
  dependency-version: 2.10.33
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: postcss-url
  dependency-version: 10.1.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-17 19:09:11 -04:00
Julien Voisin dfdd2d1510 perf(internal): cut allocations in computeXFFHeader (#1692)
This commit addresses two allocation issues:

- make([]string, 0, 4) was immediately orphaned by strings.Split
  whenever an X-Forwarded-For header is present (the common
  behind-a-proxy case), wasting an allocation per request.
- append([]string{ip}, forwardedList...) prepended a freshly
  allocated slice on every loop iteration in O(n²), defeating the
  pre-sized make two lines above.

This commit builds the chain with cheap appends into the pre-sized slice
and flip it once with slices.Reverse instead.

On a local benchmark with a 5-hop chain: 512->384 B/op (-25%), 12->7 allocs/op
(-42%), ~20% faster. As computeXFFHeader runs on every proxied request via the
XForwardedForUpdate middleware, it's an interesting optimization.

Signed-off-by: jvoisin <julien.voisin@dustri.org>
2026-06-17 19:08:35 -04:00
Julien Voisin ec6eb3359e perf(internal/gzip): share writer pool across middleware reconstructions (#1694)
GzipMiddleware pooled its gzip.Writer in a closure-local sync.Pool
(#1654). That works only when the middleware is built once, but its
sole call site (RenderIndex) reconstructs it on every request because
the wrapped handler embeds the request-specific challenge page. The
pool was therefore created empty, used once, and garbage collected on
every challenge render, never reusing a writer. The setup-time
gzip.NewWriterLevel validation allocated a second throwaway ~1.18 MiB
writer per request on top of that.

This commit hoists the pools to a process-global sync.Map keyed by compression
level so the deflate buffers survive reconstruction, and validate the level
with a cheap range check (gzip.HuffmanOnly..BestCompression) instead of
allocating a writer to probe for an error.

Measured with the middleware constructed per request, which is the production
pattern:

  B/op       1,210,345 -> 1,930   -99.84%
  allocs/op  37        -> 14      -62%
  ns/op      ~31µs     -> ~5.9µs  ~-81%

To prove that this fixes a real issue, tests are added, as the middleware was
untested:
- gzip round-trip and non-gzip passthrough correctness
- TestGzipMiddlewareInvalidLevel: construction-time range-check panic
- TestGzipMiddlewarePoolSurvivesReconstruction: asserts via
  testing.AllocsPerRun that per-request construction stays ~14 allocs,
  not the ~37 of a defeated pool
- BenchmarkGzipMiddleware: per-request construction pattern

Signed-off-by: jvoisin <julien.voisin@dustri.org>
2026-06-17 19:07:46 -04:00
Julien Voisin 9aa9bc9483 perf: iterate s.policy.Bots by index to drop per-call heap copy (#1691)
This commit removes a per-iteration heap allocation and Bot copy, which isn't
negligible when the number of rules starts to grow.

This is based on https://github.com/TecharoHQ/anubis/pull/1639 with
fixes/suggestions from @JasonLovesDoggo
(https://github.com/TecharoHQ/anubis/pull/1639#discussion_r3296352016)

Signed-off-by: jvoisin <julien.voisin@dustri.org>
Signed-off-by: Xe Iaso <xe.iaso@techaro.lol>
Co-authored-by: Xe Iaso <xe.iaso@techaro.lol>
2026-06-17 23:05:19 +00:00
Julien Voisin 2b7eb8a128 refactor(honeypot/naive): compute network hash once in incrementNetwork (#1693)
incrementNetwork called internal.SHA256sum(network) twice with the
same input; once for the Get key and once for the Set key. Hoist it
into a single local so the hash is computed once.

This path is gated behind a per-request sleep, so the runtime impact
is negligible; however, it lowers a tiny bit the cost of running anubis
CPU-wise, increasing also a tiny bit the cost asymmetry for crawlers.

Signed-off-by: jvoisin <julien.voisin@dustri.org>
2026-06-17 19:03:53 -04:00
Julien Voisin 9047750fbb perf: don't look into the store for OpenGraph things if it is disabled (#1690)
Signed-off-by: jvoisin <julien.voisin@dustri.org>
2026-06-17 19:01:32 -04:00
Julien Voisin e5ee34c4a2 perf(policy): only compute ja4h fingerprint when referenced (#1689)
The JA4H middleware computed a fingerprint for every request, even though
the resulting X-Http-Fingerprint-JA4H header is only consumed when a policy
references it. Benchmarks in #834 showed this added ~17µs and 59 allocs to
each request on the hot path.

Detect whether any bot rule references the header (via a headers_regex key
or a CEL expression) at config-parse time, expose it as ParsedConfig.NeedJA4H,
and only install the internal.JA4H middleware when it's actually needed.
Threshold expressions can't access request headers, so only bot rules are
scanned.

Refs: #834

Signed-off-by: jvoisin <julien.voisin@dustri.org>
2026-06-17 19:00:33 -04:00
Xe Iaso f21b5ae2c4 ci: remove spelling checks (#1688)
It seems that https://github.com/check-spelling/check-spelling/ has been
archived and this causes CI to fail. I guess we will have to keep a
beter eye on our speeling from now on until a solution can be found.

This is unfortunate, but such is life.

Signed-off-by: Xe Iaso <me@xeiaso.net>
2026-06-16 14:11:01 +00:00
Timon de Groot eb31754f05 fix: add trimpath option to artifact builds (#1682)
This removes build filesystem paths from the anubis logs

Signed-off-by: Timon de Groot <tdegroot96@gmail.com>
Signed-off-by: Xe Iaso <xe.iaso@techaro.lol>
Co-authored-by: Xe Iaso <xe.iaso@techaro.lol>
2026-06-11 15:40:28 +00:00
Timon de Groot 5db8b5c452 feat: add HttpOnly cookie option (#1679)
This adds support for enabling the HttpOnly flag for cookies. Setting
this default option value to false makes it a conservative,
backwards-compatible change.

By using the HttpOnly flag, sites stay working even when using strict
cookie consent management tools, which is frequently used in EU sites to
comply with GDPR and ePrivacy directive.

The tests for setting cookies have been merged into a single
table-driven test structure, adding a test case for toggling the
HttpOnly option, while also adding a proper assertion for the custom
expiration option.

Assisted-by: Claude Opus 4.8 via Claude Code

Signed-off-by: Timon de Groot <timon.degroot@team.blue>
2026-06-11 11:36:07 -04:00
Xe Iaso e7181a9a4b chore(xai): typo fix
Signed-off-by: Xe Iaso <me@xeiaso.net>
2026-06-06 11:25:46 -04:00
Xe Iaso 5660426700 chore: ban x.ai (#1673)
* chore: ban x.ai

Signed-off-by: Xe Iaso <me@xeiaso.net>

* chore: spelling

Signed-off-by: Xe Iaso <me@xeiaso.net>

* chore: spelling

Signed-off-by: Xe Iaso <me@xeiaso.net>

---------

Signed-off-by: Xe Iaso <me@xeiaso.net>
2026-06-06 10:31:24 -04:00
Xe Iaso 44d5fa3ce0 chore: use Go stdlib version stamping (#1665)
* chore: use Go stdlib version stamping

Signed-off-by: Xe Iaso <me@xeiaso.net>

* chore: spelling

Signed-off-by: Xe Iaso <me@xeiaso.net>

---------

Signed-off-by: Xe Iaso <me@xeiaso.net>
2026-06-04 16:05:37 -04:00
Julien Voisin ef3ea08b79 perf(challenge/proofofwork): stream sha256 into stack buffer in Validate (#1653)
Signed-off-by: jvoisin <julien.voisin@dustri.org>
Co-authored-by: Jason Cameron <git@jasoncameron.dev>
2026-06-03 11:35:28 -04:00
Julien Voisin a08b0f4262 perf: enable uuid randomness pool and minor cleanups (#1652)
cmd/anubis: call uuid.EnableRandPool() at the top of main. The pool
batches crypto/rand reads internally, dramatically reducing per-call
syscall overhead for UUID generation. UUIDs are produced on every
issued challenge (NewV7, 3.7 times faster, down to zero allocation) and on
every challenge page render (NewString, 1.6 times faster, 1 fewer allocation).
The pool is non-cryptographic-key material, PoW challenge bytes and signing
keys still go directly to crypto/rand.

lib/anubis.go: three trivial optimizations in issueChallenge and
maybeReverseProxy, reducing the amount of allocations by 2%, which isn't much
but since the changes are trivial:

  - fmt.Sprintf("%x", randomData) -> hex.EncodeToString(randomData)
  - cache uuid.UUID.String() once instead of calling it three times
  - fmt.Sprintf("ogtags:allow:%s%s", ...) -> string concat

Signed-off-by: jvoisin <julien.voisin@dustri.org>
Signed-off-by: Xe Iaso <xe.iaso@techaro.lol>
Co-authored-by: Xe Iaso <xe.iaso@techaro.lol>
2026-05-30 01:05:01 -04:00
Julien Voisin 3dc962b301 perf(internal/gzip): pool *gzip.Writer per middleware instance (#1654)
gzip.NewWriterLevel allocates fresh deflate window and hash table
buffers (~1.18 MiB) on every request. This commit pools them in a closure-local
sync.Pool so each middleware instance reuses its writers.

The level is validated once at setup (NewWriterLevel against
io.Discard); pooled writers are reset to io.Discard on Put so the
pool doesn't pin response writers between requests.

Only call site is RenderIndex (lib/http.go), which serves the
challenge page, so this directly cuts the per-challenge allocation
footprint.

I benchmarked the change using the following benchmark,
put in the commit message instead of in a file since it's pretty much useless
outside of this particular change.

```
package internal

import (
	"io"
	"net/http"
	"net/http/httptest"
	"testing"
)

func BenchmarkGzipMiddleware(b *testing.B) {
	payload := make([]byte, 4096)
	for i := range payload {
		payload[i] = byte(i)
	}

	inner := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Write(payload)
	})
	h := GzipMiddleware(1, inner)

	b.ReportAllocs()
	b.RunParallel(func(pb *testing.PB) {
		req := httptest.NewRequest(http.MethodGet, "/", nil)
		req.Header.Set("Accept-Encoding", "gzip")
		for pb.Next() {
			rec := httptest.NewRecorder()
			h.ServeHTTP(rec, req)
			io.Copy(io.Discard, rec.Body)
		}
	})
}
```

The results are pretty nice:

Benchmarks (Linux arm64, count=10, benchstat, vs origin/main):

  GzipMiddleware-8  sec/op     158.8µs ± 4%  ->   5.2µs ± 3%  -96.72% (p=0.000)
  GzipMiddleware-8  B/op       1180.6 KiB    ->   1.9 KiB     -99.84% (p=0.000)
  GzipMiddleware-8  allocs/op  32            ->  13           -59.38% (p=0.000)

Signed-off-by: jvoisin <julien.voisin@dustri.org>
2026-05-30 00:52:37 -04:00
Xe Iaso 926f3d1d0e fix: small security fixes (#1651)
This is based on private evaluation of a prerelease security product.
I cannot comment further other than I am impressed by its output.

This commit is a squash of several commits. The impactful commits
have details underneath markdown heading twos.

## fix(metrics): don't expose pprof by default

pprof[1] is the Go standard library profiling toolkit. It is invaluable
for diagnosing how Go programs perform in the wild. However it also is
able to expose secret data set with command line flags. This is not
ideal and should be mitigated by correctly configured firewall rules. We
don't live in a world where people correctly configure firewall rules,
so we have to fix things for people. Welcome to 2026.

[1]: https://pkg.go.dev/runtime/pprof

Ref: AWOO-001

## fix(honeypot/naive): cap r9k delay to one second

Otherwise this can get unbounded, which can cause problems with lesser
HTTP proxies such as Apache.

Ref: AWOO-002

## fix(policy): mend an edge case with subrequest auth and query strings

This fixes an unlikely edge case where using subrequest auth and query
strings with path based filtering can cause reality to differ from
administrator intent. This effectively strips the query string from
subrequest auth checks. This deficiency should be fixed in the future.

Ref: AWOO-004

## fix(expressions): mend possible nil pointer deref edge case

If Anubis just started up, load averages may not be set in memory. This
can cause a nil pointer dereference which could fail requests with weird
errors until the async thread sets the load averages.

Ref: AWOO-005

## fix(lib): mend case where domainless redirects could allow cross-domain redirects

Ref: AWOO-009

## fix(expressions): validate randInt bounds before rand.IntN

Non-positive or platform-overflowing arguments to the CEL randInt
helper used to reach rand.IntN unchecked, surfacing a CEL evaluator
error during request processing when policies passed
attacker-influenced values (e.g. contentLength). Reject non-positive
bounds and detect int narrowing explicitly, returning a typed CEL
error in both cases.

Ref: AWOO-010

Signed-off-by: Xe Iaso <xe.iaso@techaro.lol>
2026-05-30 00:48:43 -04:00
Julien Voisin 04b3a835cd perf(lib): iterate s.policy.Bots by index to drop per-call heap copy (#1639)
Signed-off-by: jvoisin <julien.voisin@dustri.org>
2026-05-28 15:35:14 +00:00
dependabot[bot] 63d517c34d build(deps): bump the npm group across 1 directory with 6 updates (#1646)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Jason Cameron <git@jasoncameron.dev>
2026-05-25 01:40:55 -04:00
Xe Iaso b57508afcd fix(honeypot/naive): apply robot9001 style delays (#1632)
Currently the honeypotting feature has no limits or delays anywhere and
uses that to feed an internal greylist of IP networks. This can cause
issues such as in #1613 where Claude's crawler seemed to pick up on it
and egress data at over one megabit per second until the administrator
noticed and blocked the address range.

This takes a different approach by inspiration of how the classic #xkcd
IRC bot Robot9000 works. The first time a given IPv4 /24 or IPv6 /48
visits a honepot page, Anubis sleeps for 1 millisecond. The second it
sleeps for two milliseconds. The third is four milliseconds and so on.
The goal of this is to make the scraping inherently self-limiting such
that the scrapers go off in their own corner where they won't really
hurt anyone.

Let's see if this works out according to keikaku.

Ref: https://github.com/TecharoHQ/anubis/issues/1613

Signed-off-by: Xe Iaso <me@xeiaso.net>
2026-05-15 17:56:37 -04:00
Xe Iaso 276b537776 fix(policy): correctly wire subrequest mode through CEL/path checkers (#1630)
* fix(policy): correctly wire subrequest mode through CEL/path checkers

Previously Anubis only checked for the X-Original-Url when using
subrequest mode. This header is used by the example nginx config to pass
the request path through from the original client request to Anubis in
order to do path-based filtering.

According to facts and circumstances, Traefik hardcodes its own
headers[1]:

```text
httpdebug-1  | GET /.within.website/x/cmd/anubis/api/check
httpdebug-1  | X-Forwarded-Method: GET
httpdebug-1  | X-Forwarded-Proto: http
httpdebug-1  | X-Forwarded-Server: b9a5d299c929
httpdebug-1  | X-Forwarded-Port: 8080
httpdebug-1  | X-Forwarded-Uri: /
httpdebug-1  | X-Real-Ip: 172.18.0.1
httpdebug-1  | Accept-Encoding: gzip
httpdebug-1  | User-Agent: curl/8.20.0
httpdebug-1  | Accept: */*
httpdebug-1  | X-Forwarded-For: 172.18.0.1
httpdebug-1  | X-Forwarded-Host: localhost:8080
```

As a result, this means that path-based filtering did not work.

This commit fixes this issue by amending how path based checking logic
works:

* For CEL based checks, this pipes through the `subrequestMode` flag from
  main and alters the behaviour if either `X-Original-Url` or
  `X-Forwarded-Url` are found. These values are currently hardcoded for
  convenience but probably need to be made configurable in the policy
  file at a future date.
* For path-based checks, this uses the existing `subrequestMode` flag
  from main and adds `X-Forwarded-Url` to the list of headers it checks.

A smoke test was added to make sure that traefik in this mode continues
to work. Thank you https://github.com/flifloo for filing a detailed
issue with the relevant configuration fragments. Those configuration
fragments formed the core of this smoke test.

[1]: https://doc.traefik.io/traefik/v3.4/middlewares/http/forwardauth/

Closes: https://github.com/TecharoHQ/anubis/issues/1628
Signed-off-by: Xe Iaso <me@xeiaso.net>
Co-Authored-By: flifloo <flifloo@gmail.com>

* chore: spelling

Signed-off-by: Xe Iaso <me@xeiaso.net>

---------

Signed-off-by: Xe Iaso <me@xeiaso.net>
Co-authored-by: flifloo <flifloo@gmail.com>
2026-05-14 21:37:02 -04:00
dependabot[bot] 9f479f578a build(deps): bump github.com/go-jose/go-jose/v3 from 3.0.4 to 3.0.5 (#1629)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-14 14:07:24 -04:00
dependabot[bot] c184028d42 build(deps-dev): bump the npm group across 1 directory with 6 updates (#1621)
Bumps the npm group with 6 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli) | `20.5.0` | `20.5.3` |
| [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/config-conventional) | `20.5.0` | `20.5.3` |
| [baseline-browser-mapping](https://github.com/web-platform-dx/baseline-browser-mapping) | `2.10.15` | `2.10.27` |
| [cssnano](https://github.com/cssnano/cssnano) | `7.1.4` | `7.1.8` |
| [cssnano-preset-advanced](https://github.com/cssnano/cssnano) | `7.0.12` | `7.0.16` |
| [prettier](https://github.com/prettier/prettier) | `3.8.1` | `3.8.3` |



Updates `@commitlint/cli` from 20.5.0 to 20.5.3
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/cli/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v20.5.3/@commitlint/cli)

Updates `@commitlint/config-conventional` from 20.5.0 to 20.5.3
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v20.5.3/@commitlint/config-conventional)

Updates `baseline-browser-mapping` from 2.10.15 to 2.10.27
- [Release notes](https://github.com/web-platform-dx/baseline-browser-mapping/releases)
- [Commits](https://github.com/web-platform-dx/baseline-browser-mapping/compare/v2.10.15...v2.10.27)

Updates `cssnano` from 7.1.4 to 7.1.8
- [Release notes](https://github.com/cssnano/cssnano/releases)
- [Commits](https://github.com/cssnano/cssnano/compare/cssnano@7.1.4...cssnano@7.1.8)

Updates `cssnano-preset-advanced` from 7.0.12 to 7.0.16
- [Release notes](https://github.com/cssnano/cssnano/releases)
- [Commits](https://github.com/cssnano/cssnano/compare/cssnano-preset-advanced@7.0.12...cssnano-preset-advanced@7.0.16)

Updates `prettier` from 3.8.1 to 3.8.3
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.8.1...3.8.3)

---
updated-dependencies:
- dependency-name: "@commitlint/cli"
  dependency-version: 20.5.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: "@commitlint/config-conventional"
  dependency-version: 20.5.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: baseline-browser-mapping
  dependency-version: 2.10.27
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: cssnano
  dependency-version: 7.1.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: cssnano-preset-advanced
  dependency-version: 7.0.16
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: prettier
  dependency-version: 3.8.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Jason Cameron <git@jasoncameron.dev>
2026-05-12 16:32:01 -04:00
Xe Iaso 0491f1fac2 fix: patch GHSA-6wcg-mqvh-fcvg (#1616)
* fix: patch GHSA-6wcg-mqvh-fcvg

PR https://github.com/TecharoHQ/anubis/pull/1015 added the ability for
reverse proxies using Anubis in subrequest auth mode to look at the path
of a request as there are many rules in the wild that rely on checking
the path. This is how access to things like robots.txt or anything in the
.well-known directory is unaffected by Anubis.

However this logic was also enabled for non-subrequest deployments of Anubis,
meaning that a specially crafted request could include a /.well-known/
path in it and then get around Anubis with little effort.

This fix gates the logic behind a new plumbed variable named subrequestMode
that only fires when Anubis is running in subrequest auth mode. This
properly contains that workaround so that the logic does not fire in
most deployments.

Signed-off-by: Xe Iaso <me@xeiaso.net>

* chore: spelling

Signed-off-by: Xe Iaso <me@xeiaso.net>

---------

Signed-off-by: Xe Iaso <me@xeiaso.net>
2026-05-08 19:17:25 -04:00
Timon de Groot d3a00da448 feat: Log weight when issuing challenge (#1611)
This can come in handy when analyzing the logs

Signed-off-by: Timon de Groot <tdegroot96@gmail.com>
2026-05-05 16:57:45 +00:00
lillian-b 7e037b65e8 feat: add ASN data from Thoth to logs/metrics (#1608)
Assisted-by: Claude Sonnet 4.6 via Claude Code

Signed-off-by: Lillian Berry <lillian@star-ark.net>
Co-authored-by: Lillian Berry <lillian@star-ark.net>
2026-05-02 11:53:00 -04:00
Xe Iaso ebf9a30878 fix(metrics): bind to the right network/bindhost (#1606)
Whoops!

Closes: #1605

Signed-off-by: Xe Iaso <me@xeiaso.net>
2026-04-30 18:18:01 -04:00
Lenny f8605bcd3c fix: Thoth geoip compare (#1564)
Co-authored-by: Jason Cameron <git@jasoncameron.dev>
2026-04-24 14:37:19 +00:00
Xe Iaso 1d700a0370 fix(honeypot): remove DoS vector (#1581)
Using the User-Agent as a filtering vector for the honeypot maze was a
decent idea, however in practice it can become a DoS vector by a
malicious client adding a lot of points to Google Chrome's User-Agent
string. In practice it also seems that the worst offenders use vanilla
Google Chrome User-Agent strings as well, meaning that this backfires
horribly.

Gotta crack a few eggs to make omlettes.

Closes: #1580

Signed-off-by: Xe Iaso <me@xeiaso.net>
2026-04-23 09:08:34 -04:00
Xe Iaso 681c2cc2ed feat(metrics): basic auth support (#1579)
* feat(internal): add basic auth HTTP middleware

Signed-off-by: Xe Iaso <me@xeiaso.net>

* feat(config): add HTTP basic auth for metrics

Signed-off-by: Xe Iaso <me@xeiaso.net>

* feat(metrics): wire up basic auth

Signed-off-by: Xe Iaso <me@xeiaso.net>

* doc: document HTTP basic auth for metrics server

Signed-off-by: Xe Iaso <me@xeiaso.net>

* chore: spelling

Signed-off-by: Xe Iaso <me@xeiaso.net>

* docs(admin/policies): give people a python command

Signed-off-by: Xe Iaso <me@xeiaso.net>

---------

Signed-off-by: Xe Iaso <me@xeiaso.net>
2026-04-23 00:17:09 -04:00
Xe Iaso 8f8ae76d56 feat(metrics): enable TLS/mTLS serving support (#1576)
* feat(config): add metrics TLS configuration

Signed-off-by: Xe Iaso <me@xeiaso.net>

* feat(metrics): add naive TLS serving for metrics

Signed-off-by: Xe Iaso <me@xeiaso.net>

* feat(metrics): import keypairreloader from a private project

Signed-off-by: Xe Iaso <me@xeiaso.net>

* fix(metrics): properly surface errors with the metrics server

Signed-off-by: Xe Iaso <me@xeiaso.net>

* feat(config): add CA certificate config value

Signed-off-by: Xe Iaso <me@xeiaso.net>

* feat(metrics): enable mTLS support

Signed-off-by: Xe Iaso <me@xeiaso.net>

* doc(default-config): document how to set up TLS and mTLS

Signed-off-by: Xe Iaso <me@xeiaso.net>

* doc: document metrics TLS and mTLS

Signed-off-by: Xe Iaso <me@xeiaso.net>

* chore: spelling

Signed-off-by: Xe Iaso <me@xeiaso.net>

---------

Signed-off-by: Xe Iaso <me@xeiaso.net>
2026-04-22 19:55:09 -04:00
Benjamin Bouvier f21706eb12 feat(data): add Meta's web indexer used for AI purposes (#1573)
This indexer is documented in
https://developers.facebook.com/docs/sharing/webmasters/web-crawlers. I
saw it parsing the entirety of my Forgejo instance, so I suggest to
widely block it.

Signed-off-by: Benjamin Bouvier <benjamin@bouvier.cc>
2026-04-21 16:56:23 -04:00
Xe Iaso d5ccf9c670 feat: move metrics server config to the policy file (#1572)
* feat(config): add metrics bind config to policy file with flag hack

Signed-off-by: Xe Iaso <me@xeiaso.net>

* feat(internal): move SetupListener from main

Signed-off-by: Xe Iaso <me@xeiaso.net>

* fix(main): use internal.SetupListener

Signed-off-by: Xe Iaso <me@xeiaso.net>

* fix(config): add metrics socket mode

Signed-off-by: Xe Iaso <me@xeiaso.net>

* feat: move metrics server to a dedicated package

Signed-off-by: Xe Iaso <me@xeiaso.net>

* doc: add metrics server configuration docs

Signed-off-by: Xe Iaso <me@xeiaso.net>

* doc(default-config): add vague references to metrics server

Signed-off-by: Xe Iaso <me@xeiaso.net>

* chore: spelling

Signed-off-by: Xe Iaso <me@xeiaso.net>

---------

Signed-off-by: Xe Iaso <me@xeiaso.net>
2026-04-21 15:36:11 -04:00