Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 40 additions & 40 deletions data/botPolicies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,49 +95,49 @@ bots:
# weight:
# adjust: -10

# Assert behaviour that only genuine browsers display. This ensures that Chrome
# or Firefox versions
- name: realistic-browser-catchall
expression:
all:
- '"User-Agent" in headers'
- '( userAgent.contains("Firefox") ) || ( userAgent.contains("Chrome") ) || ( userAgent.contains("Safari") )'
- '"Accept" in headers'
- '"Sec-Fetch-Dest" in headers'
- '"Sec-Fetch-Mode" in headers'
- '"Sec-Fetch-Site" in headers'
- '"Accept-Encoding" in headers'
- '( headers["Accept-Encoding"].contains("zstd") || headers["Accept-Encoding"].contains("br") )'
- '"Accept-Language" in headers'
action: WEIGH
weight:
adjust: -10
# # Assert behaviour that only genuine browsers display. This ensures that Chrome
# # or Firefox versions
# - name: realistic-browser-catchall
# expression:
# all:
# - '"User-Agent" in headers'
# - '( userAgent.contains("Firefox") ) || ( userAgent.contains("Chrome") ) || ( userAgent.contains("Safari") )'
# - '"Accept" in headers'
# - '"Sec-Fetch-Dest" in headers'
# - '"Sec-Fetch-Mode" in headers'
# - '"Sec-Fetch-Site" in headers'
# - '"Accept-Encoding" in headers'
# - '( headers["Accept-Encoding"].contains("zstd") || headers["Accept-Encoding"].contains("br") )'
# - '"Accept-Language" in headers'
# action: WEIGH
# weight:
# adjust: -10

# The Upgrade-Insecure-Requests header is typically sent by browsers, but not always
- name: upgrade-insecure-requests
expression: '"Upgrade-Insecure-Requests" in headers'
action: WEIGH
weight:
adjust: -2
# # The Upgrade-Insecure-Requests header is typically sent by browsers, but not always
# - name: upgrade-insecure-requests
# expression: '"Upgrade-Insecure-Requests" in headers'
# action: WEIGH
# weight:
# adjust: -2

# Chrome should behave like Chrome
- name: chrome-is-proper
expression:
all:
- userAgent.contains("Chrome")
- '"Sec-Ch-Ua" in headers'
- 'headers["Sec-Ch-Ua"].contains("Chromium")'
- '"Sec-Ch-Ua-Mobile" in headers'
- '"Sec-Ch-Ua-Platform" in headers'
action: WEIGH
weight:
adjust: -5
# # Chrome should behave like Chrome
# - name: chrome-is-proper
# expression:
# all:
# - userAgent.contains("Chrome")
# - '"Sec-Ch-Ua" in headers'
# - 'headers["Sec-Ch-Ua"].contains("Chromium")'
# - '"Sec-Ch-Ua-Mobile" in headers'
# - '"Sec-Ch-Ua-Platform" in headers'
# action: WEIGH
# weight:
# adjust: -5

- name: should-have-accept
expression: '!("Accept" in headers)'
action: WEIGH
weight:
adjust: 5
# - name: should-have-accept
# expression: '!("Accept" in headers)'
# action: WEIGH
# weight:
# adjust: 5

# Generic catchall rule
- name: generic-browser
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ require (
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3
github.com/joho/godotenv v1.5.1
github.com/lum8rjack/go-ja4h v0.0.0-20250828030157-fa5266d50650
github.com/m1/gospin v0.0.0-20200506075355-4345dd621d4a
github.com/nicksnyder/go-i18n/v2 v2.6.0
github.com/playwright-community/playwright-go v0.5200.1
github.com/prometheus/client_golang v1.23.2
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKe
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
Expand Down Expand Up @@ -282,6 +283,8 @@ github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr32
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
github.com/lum8rjack/go-ja4h v0.0.0-20250828030157-fa5266d50650 h1:hhx/Mo6+Hk0mAQS5MW311ON1VlSzp0D1cYhY27IcmnI=
github.com/lum8rjack/go-ja4h v0.0.0-20250828030157-fa5266d50650/go.mod h1:bMqyXOakqQIdx82d4vcnk5TIZLptZ2gLqju9xmPrWYA=
github.com/m1/gospin v0.0.0-20200506075355-4345dd621d4a h1:1SIAGB8spa9zVw6UL59uT5xQWjQMe7EK6rw7eYA8kdI=
github.com/m1/gospin v0.0.0-20200506075355-4345dd621d4a/go.mod h1:Mxpzp00JqlLiQAoV1bOlEKWjT5wbK9/YqHqTUvcE+4I=
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=
Expand Down Expand Up @@ -374,6 +377,8 @@ github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs=
github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
Expand All @@ -382,6 +387,7 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE
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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
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=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
Expand Down
23 changes: 23 additions & 0 deletions internal/honeypot/honeypot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package honeypot

import (
"time"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)

var Timings = promauto.NewHistogramVec(prometheus.HistogramOpts{
Namespace: "anubis",
Subsystem: "honeypot",
Name: "pagegen_timings",
Comment thread Fixed
Help: "The amount of time honeypot page generation takes per method",
Buckets: prometheus.ExponentialBuckets(0.5, 2, 32),
}, []string{"method"})

type Info struct {
CreatedAt time.Time `json:"createdAt"`
UserAgent string `json:"userAgent"`
IPAddress string `json:"ipAddress"`
HitCount int `json:"hitCount"`
}
7 changes: 7 additions & 0 deletions internal/honeypot/naive/100bytes.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
html {
max-width: 70ch;
padding: 3em 1em;
margin: auto;
line-height: 1.75;
font-size: 1.25em;
}
1 change: 1 addition & 0 deletions internal/honeypot/naive/affirmations.txt

Large diffs are not rendered by default.

120 changes: 120 additions & 0 deletions internal/honeypot/naive/naive.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package naive

import (
_ "embed"
"log/slog"
"math/rand/v2"
"net/http"
"time"

"github.com/TecharoHQ/anubis/internal/honeypot"
"github.com/TecharoHQ/anubis/lib/store"
"github.com/a-h/templ"
"github.com/google/uuid"
"github.com/m1/gospin"
Comment thread Fixed
)

//go:generate go tool github.com/a-h/templ/cmd/templ generate

// XXX(Xe): All of this was generated by ChatGPT, GLM 4.6, and GPT-OSS 120b. This is pseudoprofound bullshit in spintax[1] format so that the bullshit generator can emit plausibly human-authored text while being very computationally cheap.
Comment thread Fixed
Comment thread Fixed
Comment thread Fixed
//
// It feels somewhat poetic to use spammer technology in Anubis.
Comment thread Fixed
//
// [1]: https://outboundly.ai/blogs/what-is-spintax-and-how-to-use-it/
//
//go:embed spintext.txt
var spintext string

//go:embed titles.txt
var titles string

//go:embed affirmations.txt
var affirmations string

func New(st store.Interface, lg *slog.Logger) *Impl {
spin := gospin.New(nil)
Comment thread Fixed

return &Impl{
st: st,
infos: store.JSON[honeypot.Info]{Underlying: st, Prefix: "honeypot-infos"},
spin: spin,
lg: lg.With("component", "honeypot/naive"),
}
}

type Impl struct {
st store.Interface
infos store.JSON[honeypot.Info]
spin *gospin.Spinner
Comment thread Fixed
lg *slog.Logger
}

func (i *Impl) makeAffirmations() []string {
result, err := i.spin.SpinN(affirmations, rand.IntN(5)+1)
if err != nil {
i.lg.Debug("can't spin affirmations, using fallback", "err", err)
return []string{uuid.NewString()}
}

return result
}

func (i *Impl) makeSpins() []string {
result, err := i.spin.SpinN(spintext, rand.IntN(8)+8)
if err != nil {
i.lg.Debug("can't spin text, using fallback", "err", err)
return []string{uuid.NewString()}
}

return result
}

func (i *Impl) makeTitle() string {
result, err := i.spin.Spin(titles)
if err != nil {
i.lg.Debug("can't spin titles, using fallback", "err", err)
return uuid.NewString()
}

return result
}

func (i *Impl) ServeHTTP(w http.ResponseWriter, r *http.Request) {
t0 := time.Now()

id := r.PathValue("id")
if id == "" {
id = uuid.NewString()
}

stage := r.PathValue("stage")
if stage == "init" {
i.lg.Debug("found new entrance point", "id", id, "userAgent", r.UserAgent(), "ip", r.Header.Get("X-Real-Ip"))
}

spins := i.makeSpins()
affirmations := i.makeAffirmations()
title := i.makeTitle()

var links []link
for _, affirmation := range affirmations {
links = append(links, link{
href: uuid.NewString(),
body: affirmation,
})
}

templ.Handler(
base(title, i.maze(spins, links)),
templ.WithStreaming(),
templ.WithStatus(http.StatusOK),
).ServeHTTP(w, r)

t1 := time.Since(t0)
honeypot.Timings.WithLabelValues("naive").Observe(float64(t1.Milliseconds()))
}

type link struct {
href string
body string
}
36 changes: 36 additions & 0 deletions internal/honeypot/naive/page.templ
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package naive

import "fmt"

templ base(title string, body templ.Component) {
<!DOCTYPE html>
<html>
<head>
<style>
html {
max-width: 70ch;
padding: 3em 1em;
margin: auto;
line-height: 1.75;
font-size: 1.25em;
}
</style>
<title>{ title }</title>
</head>
<body>
<h1>{ title }</h1>
@body
</body>
</html>
}

templ (i Impl) maze(body []string, links []link) {
for _, paragraph := range body {
<p>{ paragraph }</p>
}
<ul>
for _, link := range links {
<li><a href={ templ.SafeURL(fmt.Sprintf("./%s", link.href)) }></a>{ link.body }</li>
}
</ul>
}
Loading
Loading