mirror of
https://github.com/TecharoHQ/anubis.git
synced 2026-04-05 08:18:17 +00:00
Compare commits
6 Commits
Xe/honeypo
...
Xe/no-more
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7f54aeb7d | ||
|
|
9d9be61c24 | ||
|
|
535ed74b17 | ||
|
|
ba8a1b7caf | ||
|
|
40afc13d7f | ||
|
|
122e4bc072 |
6
.github/actions/spelling/allow.txt
vendored
6
.github/actions/spelling/allow.txt
vendored
@@ -12,3 +12,9 @@ maintnotifications
|
||||
azurediamond
|
||||
cooldown
|
||||
verifyfcrdns
|
||||
Spintax
|
||||
spintax
|
||||
clampip
|
||||
pseudoprofound
|
||||
reimagining
|
||||
iocaine
|
||||
|
||||
11
.github/actions/spelling/expect.txt
vendored
11
.github/actions/spelling/expect.txt
vendored
@@ -1,4 +1,3 @@
|
||||
|
||||
acs
|
||||
Actorified
|
||||
actorifiedstore
|
||||
@@ -398,3 +397,13 @@ Zenos
|
||||
zizmor
|
||||
zombocom
|
||||
zos
|
||||
GLM
|
||||
iocaine
|
||||
nikandfor
|
||||
pagegen
|
||||
pseudoprofound
|
||||
reimagining
|
||||
Rhul
|
||||
shoneypot
|
||||
spammer
|
||||
Y'shtola
|
||||
|
||||
@@ -95,50 +95,6 @@ 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
|
||||
|
||||
# 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
|
||||
|
||||
- name: should-have-accept
|
||||
expression: '!("Accept" in headers)'
|
||||
action: WEIGH
|
||||
weight:
|
||||
adjust: 5
|
||||
|
||||
# Generic catchall rule
|
||||
- name: generic-browser
|
||||
user_agent_regex: >-
|
||||
|
||||
@@ -79,50 +79,6 @@
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
# Generic catchall rule
|
||||
- name: generic-browser
|
||||
user_agent_regex: >-
|
||||
|
||||
@@ -27,6 +27,14 @@ Anubis is back and better than ever! Lots of minor fixes with some big ones inte
|
||||
- Add support to simple Valkey/Redis cluster mode
|
||||
- Open Graph passthrough now reuses the configured target Host/SNI/TLS settings, so metadata fetches succeed when the upstream certificate differs from the public domain. ([1283](https://github.com/TecharoHQ/anubis/pull/1283))
|
||||
- Stabilize the CVE-2025-24369 regression test by always submitting an invalid proof instead of relying on random POW failures.
|
||||
- Refine the check that ensures the presence of the Accept header to avoid breaking docker clients.
|
||||
- Removed rules intended to reward actual browsers due to abuse in the wild.
|
||||
|
||||
### Dataset poisoning
|
||||
|
||||
Anubis has the ability to engage in [dataset poisoning attacks](https://www.anthropic.com/research/small-samples-poison) using the [dataset poisoning subsystem](./admin/honeypot/overview.mdx). This allows every Anubis instance to be a honeypot to attract and flag abusive scrapers so that no administrator action is required to ban them.
|
||||
|
||||
There is much more information about this feature in [the dataset poisoning subsystem documentation](./admin/honeypot/overview.mdx). Administrators that are interested in learning how this feature works should consult that documentation.
|
||||
|
||||
### Deprecate `report_as` in challenge configuration
|
||||
|
||||
|
||||
8
docs/docs/admin/honeypot/_category_.json
Normal file
8
docs/docs/admin/honeypot/_category_.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"label": "Honeypot",
|
||||
"position": 40,
|
||||
"link": {
|
||||
"type": "generated-index",
|
||||
"description": "Honeypot features in Anubis, allowing Anubis to passively detect malicious crawlers."
|
||||
}
|
||||
}
|
||||
40
docs/docs/admin/honeypot/overview.mdx
Normal file
40
docs/docs/admin/honeypot/overview.mdx
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
title: Dataset poisoning
|
||||
---
|
||||
|
||||
Anubis offers the ability to participate in [dataset poisoning](https://www.anthropic.com/research/small-samples-poison) attacks similar to what [iocaine](https://iocaine.madhouse-project.org/) and other similar tools offer. Currently this is in a preview state where a lot of details are hard-coded in order to test the viability of this approach.
|
||||
|
||||
In essence, when Anubis challenge and error pages are rendered they include a small bit of HTML code that browsers will ignore but scrapers will interpret as a link to ingest. This will then create a small forest of recursive nothing pages that are designed according to the following principles:
|
||||
|
||||
- These pages are _cheap_ to render, rendering in at most ten milliseconds on decently specced hardware.
|
||||
- These pages are _vacuous_, meaning that they essentially are devoid of content such that a human would find it odd and click away, but a scraper would not be able to know that and would continue through the forest.
|
||||
- These pages are _fairly large_ so that scrapers don't think that the pages are error pages or are otherwise devoid of content.
|
||||
- These pages are _fully self-contained_ so that they load fast without incurring additional load from resource fetches.
|
||||
|
||||
In this limited preview state, Anubis generates pages using [spintax](https://outboundly.ai/blogs/what-is-spintax-and-how-to-use-it/). Spintax is a syntax that is used to create different variants of utterances for use in marketing messages and email spam that evades word filtering. In its current form, Anubis' dataset poisoning has AI generated spintax that generates vapid LinkedIn posts with some western occultism thrown in for good measure. This results in utterances like the following:
|
||||
|
||||
> There's a moment when visionaries are being called to realize that the work can't be reduced to optimization, but about resonance. We don't transform products by grinding endlessly, we do it by holding the vision. Because meaning can't be forced, it unfolds over time when culture are in integrity. This moment represents a fundamental reimagining in how we think about work. This isn't a framework, it's a lived truth that requires courage. When we get honest, we activate nonlinear growth that don't show up in dashboards, but redefine success anyway.
|
||||
|
||||
This should be fairly transparent to humans that this is pseudoprofound anti-content and is a signal to click away.
|
||||
|
||||
## Plans
|
||||
|
||||
Future versions of this feature will allow for more customization. In the near future this will be configurable via the following mechanisms:
|
||||
|
||||
- WebAssembly logic for customizing how the poisoning data is generated (with examples including the existing spintax method).
|
||||
- Weight thresholds and logic for how they are interpreted by Anubis.
|
||||
- Other configuration settings as facts and circumstances dictate.
|
||||
|
||||
## Implementation notes
|
||||
|
||||
In its current implementation, the Anubis dataset poisoning feature has the following flaws that may hinder production deployments:
|
||||
|
||||
- All Anubis instances use the same method for generating dataset poisoning information. This may be easy for malicious actors to detect and ignore.
|
||||
- Anubis dataset poisoning routes are under the `/.within.website/x/cmd/anubis` URL hierarchy. This may be easy for malicious actors to detect and ignore.
|
||||
|
||||
Right now Anubis assigns 30 weight points if the following criteria are met:
|
||||
|
||||
- A client's User-Agent has been observed in the dataset poisoning maze at least 25 times.
|
||||
- The network-clamped IP address (/24 for IPv4 and /48 for IPv6) has been observed in the dataset poisoning maze at least 25 times.
|
||||
|
||||
Additionally, when any given client by both User-Agent and network-clamped IP address has been observed, Anubis will emit log lines warning about it so that administrative action can be taken up to and including [filing abuse reports with the network owner](/blog/2025/file-abuse-reports).
|
||||
1
go.mod
1
go.mod
@@ -20,6 +20,7 @@ require (
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/lum8rjack/go-ja4h v0.0.0-20250828030157-fa5266d50650
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.0
|
||||
github.com/nikandfor/spintax v0.0.0-20181023094358-fc346b245bb3
|
||||
github.com/playwright-community/playwright-go v0.5200.1
|
||||
github.com/prometheus/client_golang v1.23.2
|
||||
github.com/redis/go-redis/v9 v9.17.2
|
||||
|
||||
2
go.sum
2
go.sum
@@ -320,6 +320,8 @@ github.com/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0
|
||||
github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM=
|
||||
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/nikandfor/spintax v0.0.0-20181023094358-fc346b245bb3 h1:foZ9X1bz2KmW7b8Yx5V0LAQKhTazdllv5rnGUe6iGTY=
|
||||
github.com/nikandfor/spintax v0.0.0-20181023094358-fc346b245bb3/go.mod h1:wwDYKfVF3WHdY0rugsAZoIpyQjDA3bn9wEzo/QXPx1Y=
|
||||
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
|
||||
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
|
||||
33
internal/clampip.go
Normal file
33
internal/clampip.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package internal
|
||||
|
||||
import "net/netip"
|
||||
|
||||
func ClampIP(addr netip.Addr) (netip.Prefix, bool) {
|
||||
switch {
|
||||
case addr.Is4():
|
||||
result, err := addr.Prefix(24)
|
||||
if err != nil {
|
||||
return netip.Prefix{}, false
|
||||
}
|
||||
return result, true
|
||||
|
||||
case addr.Is4In6():
|
||||
// Extract the IPv4 address from IPv4-mapped IPv6 and clamp it
|
||||
ipv4 := addr.Unmap()
|
||||
result, err := ipv4.Prefix(24)
|
||||
if err != nil {
|
||||
return netip.Prefix{}, false
|
||||
}
|
||||
return result, true
|
||||
|
||||
case addr.Is6():
|
||||
result, err := addr.Prefix(48)
|
||||
if err != nil {
|
||||
return netip.Prefix{}, false
|
||||
}
|
||||
return result, true
|
||||
|
||||
default:
|
||||
return netip.Prefix{}, false
|
||||
}
|
||||
}
|
||||
274
internal/clampip_test.go
Normal file
274
internal/clampip_test.go
Normal file
@@ -0,0 +1,274 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestClampIP(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
// IPv4 addresses
|
||||
{
|
||||
name: "IPv4 normal address",
|
||||
input: "192.168.1.100",
|
||||
expected: "192.168.1.0/24",
|
||||
},
|
||||
{
|
||||
name: "IPv4 boundary - network address",
|
||||
input: "192.168.1.0",
|
||||
expected: "192.168.1.0/24",
|
||||
},
|
||||
{
|
||||
name: "IPv4 boundary - broadcast address",
|
||||
input: "192.168.1.255",
|
||||
expected: "192.168.1.0/24",
|
||||
},
|
||||
{
|
||||
name: "IPv4 class A address",
|
||||
input: "10.0.0.1",
|
||||
expected: "10.0.0.0/24",
|
||||
},
|
||||
{
|
||||
name: "IPv4 loopback",
|
||||
input: "127.0.0.1",
|
||||
expected: "127.0.0.0/24",
|
||||
},
|
||||
{
|
||||
name: "IPv4 link-local",
|
||||
input: "169.254.0.1",
|
||||
expected: "169.254.0.0/24",
|
||||
},
|
||||
{
|
||||
name: "IPv4 public address",
|
||||
input: "203.0.113.1",
|
||||
expected: "203.0.113.0/24",
|
||||
},
|
||||
|
||||
// IPv6 addresses
|
||||
{
|
||||
name: "IPv6 normal address",
|
||||
input: "2001:db8::1",
|
||||
expected: "2001:db8::/48",
|
||||
},
|
||||
{
|
||||
name: "IPv6 with full expansion",
|
||||
input: "2001:0db8:0000:0000:0000:0000:0000:0001",
|
||||
expected: "2001:db8::/48",
|
||||
},
|
||||
{
|
||||
name: "IPv6 loopback",
|
||||
input: "::1",
|
||||
expected: "::/48",
|
||||
},
|
||||
{
|
||||
name: "IPv6 unspecified address",
|
||||
input: "::",
|
||||
expected: "::/48",
|
||||
},
|
||||
{
|
||||
name: "IPv6 link-local",
|
||||
input: "fe80::1",
|
||||
expected: "fe80::/48",
|
||||
},
|
||||
{
|
||||
name: "IPv6 unique local",
|
||||
input: "fc00::1",
|
||||
expected: "fc00::/48",
|
||||
},
|
||||
{
|
||||
name: "IPv6 documentation prefix",
|
||||
input: "2001:db8:abcd:ef01::1234",
|
||||
expected: "2001:db8:abcd::/48",
|
||||
},
|
||||
{
|
||||
name: "IPv6 global unicast",
|
||||
input: "2606:4700:4700::1111",
|
||||
expected: "2606:4700:4700::/48",
|
||||
},
|
||||
{
|
||||
name: "IPv6 multicast",
|
||||
input: "ff02::1",
|
||||
expected: "ff02::/48",
|
||||
},
|
||||
|
||||
// IPv4-mapped IPv6 addresses
|
||||
{
|
||||
name: "IPv4-mapped IPv6 address",
|
||||
input: "::ffff:192.168.1.100",
|
||||
expected: "192.168.1.0/24",
|
||||
},
|
||||
{
|
||||
name: "IPv4-mapped IPv6 with different format",
|
||||
input: "::ffff:10.0.0.1",
|
||||
expected: "10.0.0.0/24",
|
||||
},
|
||||
{
|
||||
name: "IPv4-mapped IPv6 loopback",
|
||||
input: "::ffff:127.0.0.1",
|
||||
expected: "127.0.0.0/24",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
addr := netip.MustParseAddr(tt.input)
|
||||
|
||||
result, ok := ClampIP(addr)
|
||||
if !ok {
|
||||
t.Fatalf("ClampIP(%s) returned false, want true", tt.input)
|
||||
}
|
||||
|
||||
if result.String() != tt.expected {
|
||||
t.Errorf("ClampIP(%s) = %s, want %s", tt.input, result.String(), tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClampIPSuccess(t *testing.T) {
|
||||
// Test that valid inputs return success
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
}{
|
||||
{
|
||||
name: "IPv4 address",
|
||||
input: "192.168.1.100",
|
||||
},
|
||||
{
|
||||
name: "IPv6 address",
|
||||
input: "2001:db8::1",
|
||||
},
|
||||
{
|
||||
name: "IPv4-mapped IPv6",
|
||||
input: "::ffff:192.168.1.100",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
addr := netip.MustParseAddr(tt.input)
|
||||
|
||||
result, ok := ClampIP(addr)
|
||||
if !ok {
|
||||
t.Fatalf("ClampIP(%s) returned false, want true", tt.input)
|
||||
}
|
||||
|
||||
// For valid inputs, we should get the clamped prefix
|
||||
if addr.Is4() || addr.Is4In6() {
|
||||
if result.Bits() != 24 {
|
||||
t.Errorf("Expected 24 bits for IPv4, got %d", result.Bits())
|
||||
}
|
||||
} else if addr.Is6() {
|
||||
if result.Bits() != 48 {
|
||||
t.Errorf("Expected 48 bits for IPv6, got %d", result.Bits())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClampIPZeroValue(t *testing.T) {
|
||||
// Test that when ClampIP fails, it returns zero value
|
||||
// Note: It's hard to make addr.Prefix() fail with valid inputs,
|
||||
// so this test demonstrates the expected behavior
|
||||
addr := netip.MustParseAddr("192.168.1.100")
|
||||
|
||||
// Manually create a zero value for comparison
|
||||
zeroPrefix := netip.Prefix{}
|
||||
|
||||
// Call ClampIP - it should succeed with valid input
|
||||
result, ok := ClampIP(addr)
|
||||
|
||||
// Verify the function succeeded
|
||||
if !ok {
|
||||
t.Error("ClampIP should succeed with valid input")
|
||||
}
|
||||
|
||||
// Verify that the result is not a zero value
|
||||
if result == zeroPrefix {
|
||||
t.Error("Result should not be zero value for successful operation")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClampIPSpecialCases(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expectedPrefix int
|
||||
expectedNetwork string
|
||||
}{
|
||||
{
|
||||
name: "Minimum IPv4",
|
||||
input: "0.0.0.0",
|
||||
expectedPrefix: 24,
|
||||
expectedNetwork: "0.0.0.0",
|
||||
},
|
||||
{
|
||||
name: "Maximum IPv4",
|
||||
input: "255.255.255.255",
|
||||
expectedPrefix: 24,
|
||||
expectedNetwork: "255.255.255.0",
|
||||
},
|
||||
{
|
||||
name: "Minimum IPv6",
|
||||
input: "::",
|
||||
expectedPrefix: 48,
|
||||
expectedNetwork: "::",
|
||||
},
|
||||
{
|
||||
name: "Maximum IPv6 prefix part",
|
||||
input: "ffff:ffff:ffff::",
|
||||
expectedPrefix: 48,
|
||||
expectedNetwork: "ffff:ffff:ffff::",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
addr := netip.MustParseAddr(tt.input)
|
||||
|
||||
result, ok := ClampIP(addr)
|
||||
if !ok {
|
||||
t.Fatalf("ClampIP(%s) returned false, want true", tt.input)
|
||||
}
|
||||
|
||||
if result.Bits() != tt.expectedPrefix {
|
||||
t.Errorf("ClampIP(%s) bits = %d, want %d", tt.input, result.Bits(), tt.expectedPrefix)
|
||||
}
|
||||
|
||||
if result.Addr().String() != tt.expectedNetwork {
|
||||
t.Errorf("ClampIP(%s) network = %s, want %s", tt.input, result.Addr().String(), tt.expectedNetwork)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmark to ensure the function is performant
|
||||
func BenchmarkClampIP(b *testing.B) {
|
||||
ipv4 := netip.MustParseAddr("192.168.1.100")
|
||||
ipv6 := netip.MustParseAddr("2001:db8::1")
|
||||
ipv4mapped := netip.MustParseAddr("::ffff:192.168.1.100")
|
||||
|
||||
b.Run("IPv4", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ClampIP(ipv4)
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("IPv6", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ClampIP(ipv6)
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("IPv4-mapped", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ClampIP(ipv4mapped)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
@@ -13,6 +14,13 @@ import (
|
||||
"github.com/sebest/xff"
|
||||
)
|
||||
|
||||
type realIPKey struct{}
|
||||
|
||||
func RealIP(r *http.Request) (netip.Addr, bool) {
|
||||
result, ok := r.Context().Value(realIPKey{}).(netip.Addr)
|
||||
return result, ok
|
||||
}
|
||||
|
||||
// TODO: move into config
|
||||
type XFFComputePreferences struct {
|
||||
StripPrivate bool
|
||||
@@ -77,6 +85,9 @@ func RemoteXRealIP(useRemoteAddress bool, bindNetwork string, next http.Handler)
|
||||
panic(err) // this should never happen
|
||||
}
|
||||
r.Header.Set("X-Real-Ip", host)
|
||||
if addr, err := netip.ParseAddr(host); err == nil {
|
||||
r = r.WithContext(context.WithValue(r.Context(), realIPKey{}, addr))
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
@@ -89,6 +100,9 @@ func XForwardedForToXRealIP(next http.Handler) http.Handler {
|
||||
ip := xff.Parse(xffHeader)
|
||||
slog.Debug("setting X-Real-Ip from X-Forwarded-For", "to", ip, "x-forwarded-for", xffHeader)
|
||||
r.Header.Set("X-Real-Ip", ip)
|
||||
if addr, err := netip.ParseAddr(ip); err == nil {
|
||||
r = r.WithContext(context.WithValue(r.Context(), realIPKey{}, addr))
|
||||
}
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
@@ -129,8 +143,6 @@ func XForwardedForUpdate(stripPrivate bool, next http.Handler) http.Handler {
|
||||
} else {
|
||||
r.Header.Set("X-Forwarded-For", xffHeaderString)
|
||||
}
|
||||
|
||||
slog.Debug("updating X-Forwarded-For", "original", origXFFHeader, "new", xffHeaderString)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
23
internal/honeypot/honeypot.go
Normal file
23
internal/honeypot/honeypot.go
Normal file
@@ -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",
|
||||
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
internal/honeypot/naive/100bytes.css
Normal file
7
internal/honeypot/naive/100bytes.css
Normal file
@@ -0,0 +1,7 @@
|
||||
html {
|
||||
max-width: 70ch;
|
||||
padding: 3em 1em;
|
||||
margin: auto;
|
||||
line-height: 1.75;
|
||||
font-size: 1.25em;
|
||||
}
|
||||
1
internal/honeypot/naive/affirmations.txt
Normal file
1
internal/honeypot/naive/affirmations.txt
Normal file
File diff suppressed because one or more lines are too long
205
internal/honeypot/naive/naive.go
Normal file
205
internal/honeypot/naive/naive.go
Normal file
@@ -0,0 +1,205 @@
|
||||
package naive
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"math/rand/v2"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/TecharoHQ/anubis/internal"
|
||||
"github.com/TecharoHQ/anubis/internal/honeypot"
|
||||
"github.com/TecharoHQ/anubis/lib/policy/checker"
|
||||
"github.com/TecharoHQ/anubis/lib/store"
|
||||
"github.com/a-h/templ"
|
||||
"github.com/google/uuid"
|
||||
"github.com/nikandfor/spintax"
|
||||
)
|
||||
|
||||
//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.
|
||||
//
|
||||
// It feels somewhat poetic to use spammer technology in Anubis.
|
||||
//
|
||||
// [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, error) {
|
||||
affirmation, err := spintax.Parse(affirmations)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't parse affirmations: %w", err)
|
||||
}
|
||||
|
||||
body, err := spintax.Parse(spintext)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't parse bodies: %w", err)
|
||||
}
|
||||
|
||||
title, err := spintax.Parse(titles)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't parse titles: %w", err)
|
||||
}
|
||||
|
||||
lg.Debug("initialized basic bullshit generator", "affirmations", affirmation.Count(), "bodies", body.Count(), "titles", title.Count())
|
||||
|
||||
return &Impl{
|
||||
st: st,
|
||||
infos: store.JSON[honeypot.Info]{Underlying: st, Prefix: "honeypot:info"},
|
||||
uaWeight: store.JSON[int]{Underlying: st, Prefix: "honeypot:user-agent"},
|
||||
networkWeight: store.JSON[int]{Underlying: st, Prefix: "honeypot:network"},
|
||||
affirmation: affirmation,
|
||||
body: body,
|
||||
title: title,
|
||||
lg: lg.With("component", "honeypot/naive"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type Impl struct {
|
||||
st store.Interface
|
||||
infos store.JSON[honeypot.Info]
|
||||
uaWeight store.JSON[int]
|
||||
networkWeight store.JSON[int]
|
||||
lg *slog.Logger
|
||||
|
||||
affirmation, body, title spintax.Spintax
|
||||
}
|
||||
|
||||
func (i *Impl) incrementUA(ctx context.Context, userAgent string) int {
|
||||
result, _ := i.uaWeight.Get(ctx, internal.SHA256sum(userAgent))
|
||||
result++
|
||||
i.uaWeight.Set(ctx, internal.SHA256sum(userAgent), result, time.Hour)
|
||||
return result
|
||||
}
|
||||
|
||||
func (i *Impl) incrementNetwork(ctx context.Context, network string) int {
|
||||
result, _ := i.networkWeight.Get(ctx, internal.SHA256sum(network))
|
||||
result++
|
||||
i.networkWeight.Set(ctx, internal.SHA256sum(network), result, time.Hour)
|
||||
return result
|
||||
}
|
||||
|
||||
func (i *Impl) CheckUA() checker.Impl {
|
||||
return checker.Func(func(r *http.Request) (bool, error) {
|
||||
result, _ := i.uaWeight.Get(r.Context(), internal.SHA256sum(r.UserAgent()))
|
||||
if result >= 25 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (i *Impl) CheckNetwork() checker.Impl {
|
||||
return checker.Func(func(r *http.Request) (bool, error) {
|
||||
result, _ := i.uaWeight.Get(r.Context(), internal.SHA256sum(r.UserAgent()))
|
||||
if result >= 25 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (i *Impl) Hash() string {
|
||||
return internal.SHA256sum("naive honeypot")
|
||||
}
|
||||
|
||||
func (i *Impl) makeAffirmations() []string {
|
||||
count := rand.IntN(5) + 1
|
||||
|
||||
var result []string
|
||||
for j := 0; j < count; j++ {
|
||||
result = append(result, i.affirmation.Spin())
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (i *Impl) makeSpins() []string {
|
||||
count := rand.IntN(5) + 1
|
||||
|
||||
var result []string
|
||||
for j := 0; j < count; j++ {
|
||||
result = append(result, i.body.Spin())
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (i *Impl) makeTitle() string {
|
||||
return i.title.Spin()
|
||||
}
|
||||
|
||||
func (i *Impl) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
t0 := time.Now()
|
||||
lg := internal.GetRequestLogger(i.lg, r)
|
||||
|
||||
id := r.PathValue("id")
|
||||
if id == "" {
|
||||
id = uuid.NewString()
|
||||
}
|
||||
|
||||
realIP, _ := internal.RealIP(r)
|
||||
if !realIP.IsValid() {
|
||||
realIP = netip.MustParseAddr(r.Header.Get("X-Real-Ip"))
|
||||
}
|
||||
|
||||
network, ok := internal.ClampIP(realIP)
|
||||
if !ok {
|
||||
lg.Error("clampIP failed", "output", network, "ok", ok)
|
||||
http.Error(w, "The cake is a lie", http.StatusTeapot)
|
||||
return
|
||||
}
|
||||
|
||||
networkCount := i.incrementNetwork(r.Context(), network.String())
|
||||
uaCount := i.incrementUA(r.Context(), r.UserAgent())
|
||||
|
||||
stage := r.PathValue("stage")
|
||||
|
||||
if stage == "init" {
|
||||
lg.Debug("found new entrance point", "id", id, "stage", stage, "userAgent", r.UserAgent(), "clampedIP", network)
|
||||
} else {
|
||||
switch {
|
||||
case networkCount%256 == 0, uaCount%256 == 0:
|
||||
lg.Warn("found possible crawler", "id", id, "network", network)
|
||||
}
|
||||
}
|
||||
|
||||
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
internal/honeypot/naive/page.templ
Normal file
36
internal/honeypot/naive/page.templ
Normal file
@@ -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)) }>{ link.body }</a></li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
160
internal/honeypot/naive/page_templ.go
generated
Normal file
160
internal/honeypot/naive/page_templ.go
generated
Normal file
@@ -0,0 +1,160 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.960
|
||||
package naive
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import "fmt"
|
||||
|
||||
func base(title string, body templ.Component) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html><head><style>\n html {\n max-width: 70ch;\n padding: 3em 1em;\n margin: auto;\n line-height: 1.75;\n font-size: 1.25em;\n }\n </style><title>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `page.templ`, Line: 18, Col: 17}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</title></head><body><h1>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `page.templ`, Line: 21, Col: 14}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</h1>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = body.Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</body></html>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (i Impl) maze(body []string, links []link) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var4 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var4 == nil {
|
||||
templ_7745c5c3_Var4 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
for _, paragraph := range body {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(paragraph)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `page.templ`, Line: 29, Col: 16}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<ul>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, link := range links {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<li><a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 templ.SafeURL
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("./%s", link.href)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `page.templ`, Line: 33, Col: 62}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(link.body)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `page.templ`, Line: 33, Col: 76}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</a></li>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</ul>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
1
internal/honeypot/naive/spintext.txt
Normal file
1
internal/honeypot/naive/spintext.txt
Normal file
File diff suppressed because one or more lines are too long
1
internal/honeypot/naive/titles.txt
Normal file
1
internal/honeypot/naive/titles.txt
Normal file
File diff suppressed because one or more lines are too long
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/TecharoHQ/anubis"
|
||||
"github.com/TecharoHQ/anubis/data"
|
||||
"github.com/TecharoHQ/anubis/internal"
|
||||
"github.com/TecharoHQ/anubis/internal/honeypot/naive"
|
||||
"github.com/TecharoHQ/anubis/internal/ogtags"
|
||||
"github.com/TecharoHQ/anubis/lib/challenge"
|
||||
"github.com/TecharoHQ/anubis/lib/config"
|
||||
@@ -175,6 +176,33 @@ func New(opts Options) (*Server, error) {
|
||||
registerWithPrefix(anubis.APIPrefix+"check", http.HandlerFunc(result.maybeReverseProxyHttpStatusOnly), "")
|
||||
registerWithPrefix("/", http.HandlerFunc(result.maybeReverseProxyOrPage), "")
|
||||
|
||||
mazeGen, err := naive.New(result.store, result.logger)
|
||||
if err == nil {
|
||||
registerWithPrefix(anubis.APIPrefix+"honeypot/{id}/{stage}", mazeGen, http.MethodGet)
|
||||
|
||||
opts.Policy.Bots = append(
|
||||
opts.Policy.Bots,
|
||||
policy.Bot{
|
||||
Rules: mazeGen.CheckNetwork(),
|
||||
Action: config.RuleWeigh,
|
||||
Weight: &config.Weight{
|
||||
Adjust: 30,
|
||||
},
|
||||
Name: "honeypot/network",
|
||||
},
|
||||
policy.Bot{
|
||||
Rules: mazeGen.CheckUA(),
|
||||
Action: config.RuleWeigh,
|
||||
Weight: &config.Weight{
|
||||
Adjust: 30,
|
||||
},
|
||||
Name: "honeypot/user-agent",
|
||||
},
|
||||
)
|
||||
} else {
|
||||
result.logger.Error("can't init honeypot subsystem", "err", err)
|
||||
}
|
||||
|
||||
//goland:noinspection GoBoolExpressions
|
||||
if anubis.Version == "devel" {
|
||||
// make-challenge is only used in tests. Only enable while version is devel
|
||||
|
||||
@@ -2,20 +2,21 @@
|
||||
"loading": "Ladevorgang...",
|
||||
"why_am_i_seeing": "Warum sehe ich diese Seite?",
|
||||
"protected_by": "Geschützt durch",
|
||||
"protected_from": "From",
|
||||
"made_with": "Mit ❤️ gemacht in 🇨🇦",
|
||||
"protected_from": "Von",
|
||||
"made_with": "Mit ❤️ entwickelt 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": "Letztendlich ist dies eine Platzhalterlösung, damit mehr Zeit für die Fingerabdruckerkennung und Identifizierung von Headless-Browsern (z. B. anhand ihrer Schriftwiedergabe) aufgewendet werden kann, sodass die Proof-of-Work-Seite nicht Benutzern präsentiert werden muss, die viel wahrscheinlicher legitim sind.",
|
||||
"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",
|
||||
"ai_companies_explanation": "Diese Seite wird angezeigt, da der Betreiber der Website Anubis eingerichtet hat, um sie vor aggressiven Webcrawlern von KI-Unternehmen zu schützen. Diese können Ausfälle verursachen, wodurch die Website für niemanden erreichbar ist.",
|
||||
"anubis_compromise": "Anubis stellt einen Kompromiss dar. Es verwendet eine Proof-of-Work-Methode nach dem Hashcash-Prinzip, das ursprünglich zur Bekämpfung von E-Mail-Spam entwickelt wurde. Die Idee dahinter: Für einen einzelnen Besucher ist die Verzögerung vernachlässigbar, aber massenhaftes Scraping wird dadurch aufwändig und teuer.",
|
||||
"hack_purpose": "Letztendlich ist dies eine Übergangslösung, um mehr Zeit für Browser-Fingerprinting und die Identifizierung von Headless-Browsern (z. B. anhand ihrer Schriftwiedergabe) zu gewinnen. So muss die Proof-of-Work-Seite nicht Nutzern angezeigt werden, die sehr wahrscheinlich legitim sind.",
|
||||
"simplified_explanation": "Dies ist eine Maßnahme gegen Bots und bösartige Anfragen, ähnlich einem CAPTCHA. Anstatt jedoch selbst arbeiten zu müssen, erhält dein Browser eine Rechenaufgabe, um sicherzustellen, dass es sich um einen gültigen Client handelt. Dieses Konzept nennt sich <a href=\"https://en.wikipedia.org/wiki/Proof_of_work\">Proof of Work</a>. Die Aufgabe wird in wenigen Sekunden berechnet und du erhältst Zugriff auf die Website. Danke für deine Geduld.",
|
||||
"jshelter_note": "Anubis benötigt moderne JavaScript-Features, die von Plugins wie JShelter deaktiviert werden. Bitte deaktiviere JShelter oder ähnliche Plugins für diese Domain.",
|
||||
"version_info": "Diese Website läuft mit Anubis-Version",
|
||||
"try_again": "Erneut versuchen",
|
||||
"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.",
|
||||
"go_home": "Zur Startseite",
|
||||
"contact_webmaster": "Falls du glaubst, dass es sich um einen Fehler handelt, kontaktiere bitte den Administrator unter",
|
||||
"connection_security": "Bitte warte einen Moment, während wir die Sicherheit deiner Verbindung prüfen.",
|
||||
"javascript_required": "Du musst JavaScript aktivieren, um diese Prüfung durchführen zu können. Dies ist notwendig, da KI-Unternehmen die bisherigen Regeln für das Hosting von Websites nicht mehr respektieren. Eine Lösung ohne JavaScript ist in Entwicklung.",
|
||||
"benchmark_requires_js": "Für die Nutzung des Benchmark-Tools muss JavaScript aktiviert sein.",
|
||||
"difficulty": "Schwierigkeit:",
|
||||
"algorithm": "Algorithmus:",
|
||||
"compare": "Vergleich:",
|
||||
@@ -25,42 +26,41 @@
|
||||
"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.",
|
||||
"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.",
|
||||
"access_denied": "Zugriff verweigert: Fehlercode",
|
||||
"static_check_endpoint": "Dies ist ein Endpunkt zur Prüfung durch einen Reverse-Proxy.",
|
||||
"authorization_required": "Autorisierung erforderlich",
|
||||
"cookies_disabled": "Cookies sind in deinem Browser deaktiviert. Anubis benötigt Cookies, um sicherzustellen, dass es sich um einen legitimen 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 Serverfehler: Der Administrator hat Anubis fehlerhaft konfiguriert. Bitte kontaktiere den Administrator und bitte ihn, die Logs zu prüfen.",
|
||||
"invalid_redirect": "Ungültige Weiterleitung",
|
||||
"redirect_not_parseable": "URL der Weiterleitung kann nicht verarbeitet werden",
|
||||
"redirect_domain_not_allowed": "Domain der Weiterleitung nicht erlaubt",
|
||||
"redirect_not_parseable": "Weiterleitungs-URL kann nicht verarbeitet werden",
|
||||
"redirect_domain_not_allowed": "Weiterleitungs-Domain nicht erlaubt",
|
||||
"missing_required_forwarded_headers": "Erforderliche X-Forwarded-*-Header fehlen",
|
||||
"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.",
|
||||
"client_error_browser": "Client-Fehler: Bitte stelle sicher, dass dein Browser aktuell ist, und versuche es später erneut.",
|
||||
"oh_noes": "Oh nein!",
|
||||
"benchmarking_anubis": "Benchmark wird durchgeführt!",
|
||||
"you_are_not_a_bot": "Du bist kein Bot!",
|
||||
"making_sure_not_bot": "Dein 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_crypto_error": "Dein Browser verfügt nicht über ein 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_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önnen sich jederzeit ändern. Cookie-Namen und gespeicherte Werte sind nicht 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_calculating": "Berechnung wird durchgeführt...",
|
||||
"js_context_not_secure_msg": "Bitte versuche, dich über HTTPS zu verbinden, oder weise den Administrator darauf hin, HTTPS einzurichten. Mehr Informationen: <a href=\"https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure\">MDN</a>.",
|
||||
"js_calculating": "Berechnung läuft...",
|
||||
"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_calculating_difficulty": "Berechnung wird durchgeführt...<br/>Schwierigkeit:",
|
||||
"js_challenge_error_msg": "Der Prüf-Algorithmus konnte nicht geladen werden. Bitte lade die Seite neu.",
|
||||
"js_calculating_difficulty": "Berechnung läuft...<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": "Die Prüfung dauert länger als erwartet. Bitte warte und lade die Seite nicht neu.",
|
||||
"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:",
|
||||
"missing_required_forwarded_headers": "Fehlende erforderliche X-Forwarded-* Header",
|
||||
"simplified_explanation": "Dies ist eine Maßnahme gegen Bots und bösartige Anfragen, ähnlich einem CAPTCHA. Anstatt jedoch selbst arbeiten zu müssen, erhält Ihr Browser eine Berechnungsaufgabe, die er lösen muss, um sicherzustellen, dass es sich um einen gültigen Client handelt. Dieses Konzept wird als <a href=\"https://en.wikipedia.org/wiki/Proof_of_work\">Proof of Work</a> bezeichnet. Die Aufgabe wird in wenigen Sekunden berechnet und Sie erhalten Zugriff auf die Website. Vielen Dank für Ihr Verständnis und Ihre Geduld."
|
||||
}
|
||||
"js_finished_reading": "Fertig gelesen – weiter zur Seite →",
|
||||
"js_calculation_error": "Berechnungsfehler!",
|
||||
"js_calculation_error_msg": "Fehler bei der Berechnung der Prüfung:"
|
||||
}
|
||||
|
||||
@@ -14,6 +14,14 @@ type Impl interface {
|
||||
Hash() string
|
||||
}
|
||||
|
||||
type Func func(*http.Request) (bool, error)
|
||||
|
||||
func (f Func) Check(r *http.Request) (bool, error) {
|
||||
return f(r)
|
||||
}
|
||||
|
||||
func (f Func) Hash() string { return internal.FastHash(fmt.Sprintf("%#v", f)) }
|
||||
|
||||
type List []Impl
|
||||
|
||||
// Check runs each checker in the list against the request.
|
||||
|
||||
@@ -66,6 +66,7 @@ require (
|
||||
github.com/moby/sys/atomicwriter v0.1.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.0 // indirect
|
||||
github.com/nikandfor/spintax v0.0.0-20181023094358-fc346b245bb3 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
|
||||
@@ -172,6 +172,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
|
||||
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/nikandfor/spintax v0.0.0-20181023094358-fc346b245bb3 h1:foZ9X1bz2KmW7b8Yx5V0LAQKhTazdllv5rnGUe6iGTY=
|
||||
github.com/nikandfor/spintax v0.0.0-20181023094358-fc346b245bb3/go.mod h1:wwDYKfVF3WHdY0rugsAZoIpyQjDA3bn9wEzo/QXPx1Y=
|
||||
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=
|
||||
|
||||
11
web/index.go
11
web/index.go
@@ -1,6 +1,10 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/a-h/templ"
|
||||
|
||||
"github.com/TecharoHQ/anubis/lib/challenge"
|
||||
@@ -29,3 +33,10 @@ func ErrorPage(msg, mail, code string, localizer *localization.SimpleLocalizer)
|
||||
func Bench(localizer *localization.SimpleLocalizer) templ.Component {
|
||||
return bench(localizer)
|
||||
}
|
||||
|
||||
func honeypotLink(href string) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
|
||||
fmt.Fprintf(w, `<script type="ignore"><a href="%s">Don't click me</a></script>`, href)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/TecharoHQ/anubis/lib/config"
|
||||
"github.com/TecharoHQ/anubis/lib/localization"
|
||||
"github.com/TecharoHQ/anubis/xess"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
templ base(title string, body templ.Component, impressum *config.Impressum, challenge any, ogTags map[string]string, localizer *localization.SimpleLocalizer) {
|
||||
@@ -63,6 +64,7 @@ templ base(title string, body templ.Component, impressum *config.Impressum, chal
|
||||
@templ.JSONScript("anubis_public_url", anubis.PublicUrl)
|
||||
</head>
|
||||
<body id="top">
|
||||
@honeypotLink(fmt.Sprintf("%shoneypot/%s/init", anubis.APIPrefix, uuid.NewString()))
|
||||
<main>
|
||||
<h1 id="title" class="centered-div">{ title }</h1>
|
||||
@body
|
||||
|
||||
171
web/index_templ.go
generated
171
web/index_templ.go
generated
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/TecharoHQ/anubis/lib/config"
|
||||
"github.com/TecharoHQ/anubis/lib/localization"
|
||||
"github.com/TecharoHQ/anubis/xess"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func base(title string, body templ.Component, impressum *config.Impressum, challenge any, ogTags map[string]string, localizer *localization.SimpleLocalizer) templ.Component {
|
||||
@@ -44,7 +45,7 @@ func base(title string, body templ.Component, impressum *config.Impressum, chall
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.GetLang())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 13, Col: 33}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 14, Col: 33}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -57,7 +58,7 @@ func base(title string, body templ.Component, impressum *config.Impressum, chall
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 15, Col: 17}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 16, Col: 17}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -70,7 +71,7 @@ func base(title string, body templ.Component, impressum *config.Impressum, chall
|
||||
var templ_7745c5c3_Var4 templ.SafeURL
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinURLErrs(anubis.BasePrefix + xess.URL)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 16, Col: 61}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 17, Col: 61}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -88,7 +89,7 @@ func base(title string, body templ.Component, impressum *config.Impressum, chall
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(key)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 20, Col: 24}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 21, Col: 24}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -101,7 +102,7 @@ func base(title string, body templ.Component, impressum *config.Impressum, chall
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(value)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 20, Col: 42}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 21, Col: 42}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -132,20 +133,28 @@ func base(title string, body templ.Component, impressum *config.Impressum, chall
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</head><body id=\"top\"><main><h1 id=\"title\" class=\"centered-div\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</head><body id=\"top\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = honeypotLink(fmt.Sprintf("%shoneypot/%s/init", anubis.APIPrefix, uuid.NewString())).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<main><h1 id=\"title\" class=\"centered-div\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 67, Col: 47}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 69, Col: 47}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</h1>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</h1>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -153,77 +162,77 @@ func base(title string, body templ.Component, impressum *config.Impressum, chall
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<footer><div class=\"centered-div\"><p>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<footer><div class=\"centered-div\"><p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var8 string
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("protected_by"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 72, Col: 36}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 74, Col: 36}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, " <a href=\"https://github.com/TecharoHQ/anubis\">Anubis</a> ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, " <a href=\"https://github.com/TecharoHQ/anubis\">Anubis</a> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var9 string
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("protected_from"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 72, Col: 127}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 74, Col: 127}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, " <a href=\"https://techaro.lol\">Techaro</a>. ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, " <a href=\"https://techaro.lol\">Techaro</a>. ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var10 string
|
||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("made_with"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 74, Col: 40}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 76, Col: 40}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, ".</p><p>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, ".</p><p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var11 string
|
||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("mascot_design"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 76, Col: 39}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 78, Col: 39}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, " <a href=\"https://bsky.app/profile/celphase.bsky.social\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, " <a href=\"https://bsky.app/profile/celphase.bsky.social\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var12 string
|
||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("celphase"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 76, Col: 123}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 78, Col: 123}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</a>.</p>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</a>.</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if impressum != nil {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<p>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "<p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -231,51 +240,51 @@ func base(title string, body templ.Component, impressum *config.Impressum, chall
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "-- <a href=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "-- <a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var13 templ.SafeURL
|
||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("%simprint", anubis.APIPrefix)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 80, Col: 78}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 82, Col: 78}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\">Imprint</a></p>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "\">Imprint</a></p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<p>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var14 string
|
||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("version_info"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 83, Col: 38}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 85, Col: 38}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, " <code>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, " <code>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var15 string
|
||||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(anubis.Version)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 83, Col: 63}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 85, Col: 63}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "</code>.</p></div></footer></main></body></html>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</code>.</p></div></footer></main></body></html>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -304,132 +313,132 @@ func errorPage(message, mail, code string, localizer *localization.SimpleLocaliz
|
||||
templ_7745c5c3_Var16 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<div class=\"centered-div\"><img id=\"image\" alt=\"Sad Anubis\" style=\"width:100%;max-width:256px;\" src=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<div class=\"centered-div\"><img id=\"image\" alt=\"Sad Anubis\" style=\"width:100%;max-width:256px;\" src=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var17 string
|
||||
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(anubis.BasePrefix + "/.within.website/x/cmd/anubis/static/img/reject.webp?cacheBuster=" + anubis.Version)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 93, Col: 181}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 95, Col: 181}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "\"><p>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "\"><p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var18 string
|
||||
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(message)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 94, Col: 14}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 96, Col: 14}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, ".</p>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, ".</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if code != "" {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<code><pre>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<code><pre>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var19 string
|
||||
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(code)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 96, Col: 20}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 98, Col: 20}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "</pre></code> ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</pre></code> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
if mail != "" {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<p><a href=\"/\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<p><a href=\"/\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var20 string
|
||||
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("go_home"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 100, Col: 40}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 102, Col: 40}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</a> ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</a> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var21 string
|
||||
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("contact_webmaster"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 100, Col: 81}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 102, Col: 81}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, " <a href=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, " <a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var22 templ.SafeURL
|
||||
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinURLErrs("mailto:" + templ.SafeURL(mail))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 101, Col: 45}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 103, Col: 45}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var23 string
|
||||
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(mail)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 102, Col: 11}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 104, Col: 11}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "</a></p>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "</a></p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "<p><a href=\"/\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<p><a href=\"/\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var24 string
|
||||
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("go_home"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 106, Col: 42}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 108, Col: 42}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</a></p>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "</a></p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "</div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -458,7 +467,7 @@ func StaticHappy(localizer *localization.SimpleLocalizer) templ.Component {
|
||||
templ_7745c5c3_Var25 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "<div class=\"centered-div\"><img style=\"display:none;\" style=\"width:100%;max-width:256px;\" src=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<div class=\"centered-div\"><img style=\"display:none;\" style=\"width:100%;max-width:256px;\" src=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -466,26 +475,26 @@ func StaticHappy(localizer *localization.SimpleLocalizer) templ.Component {
|
||||
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs("/.within.website/x/cmd/anubis/static/img/happy.webp?cacheBuster=" +
|
||||
anubis.Version)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 117, Col: 18}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 119, Col: 18}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "\"><p>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "\"><p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var27 string
|
||||
templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("static_check_endpoint"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 119, Col: 43}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 121, Col: 43}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "</p></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "</p></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -514,176 +523,176 @@ func bench(localizer *localization.SimpleLocalizer) templ.Component {
|
||||
templ_7745c5c3_Var28 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "<div style=\"height:20rem;display:flex\"><table style=\"margin-top:1rem;display:grid;grid-template:auto 1fr/auto auto;gap:0 0.5rem\"><thead style=\"border-bottom:1px solid black;padding:0.25rem 0;display:grid;grid-template:1fr/subgrid;grid-column:1/-1\"><tr id=\"table-header\" style=\"display:contents\"><th style=\"width:4.5rem\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "<div style=\"height:20rem;display:flex\"><table style=\"margin-top:1rem;display:grid;grid-template:auto 1fr/auto auto;gap:0 0.5rem\"><thead style=\"border-bottom:1px solid black;padding:0.25rem 0;display:grid;grid-template:1fr/subgrid;grid-column:1/-1\"><tr id=\"table-header\" style=\"display:contents\"><th style=\"width:4.5rem\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var29 string
|
||||
templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("time"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 130, Col: 51}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 132, Col: 51}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "</th><th style=\"width:4rem\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</th><th style=\"width:4rem\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var30 string
|
||||
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("iters"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 131, Col: 50}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 133, Col: 50}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</th></tr><tr id=\"table-header-compare\" style=\"display:none\"><th style=\"width:4.5rem\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "</th></tr><tr id=\"table-header-compare\" style=\"display:none\"><th style=\"width:4.5rem\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var31 string
|
||||
templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("time_a"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 134, Col: 53}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 136, Col: 53}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "</th><th style=\"width:4rem\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</th><th style=\"width:4rem\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var32 string
|
||||
templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("iters_a"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 135, Col: 52}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 137, Col: 52}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</th><th style=\"width:4.5rem\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</th><th style=\"width:4.5rem\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var33 string
|
||||
templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("time_b"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 136, Col: 53}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 138, Col: 53}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</th><th style=\"width:4rem\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "</th><th style=\"width:4rem\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var34 string
|
||||
templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("iters_b"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 137, Col: 52}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 139, Col: 52}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "</th></tr></thead> <tbody id=\"results\" style=\"padding-top:0.25rem;display:grid;grid-template-columns:subgrid;grid-auto-rows:min-content;grid-column:1/-1;row-gap:0.25rem;overflow-y:auto;font-variant-numeric:tabular-nums\"></tbody></table><div class=\"centered-div\"><img id=\"image\" style=\"width:100%;max-width:256px;\" src=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "</th></tr></thead> <tbody id=\"results\" style=\"padding-top:0.25rem;display:grid;grid-template-columns:subgrid;grid-auto-rows:min-content;grid-column:1/-1;row-gap:0.25rem;overflow-y:auto;font-variant-numeric:tabular-nums\"></tbody></table><div class=\"centered-div\"><img id=\"image\" style=\"width:100%;max-width:256px;\" src=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var35 string
|
||||
templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(anubis.BasePrefix + "/.within.website/x/cmd/anubis/static/img/pensive.webp?cacheBuster=" + anubis.Version)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 146, Col: 166}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 148, Col: 166}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "\"><p id=\"status\" style=\"max-width:256px\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "\"><p id=\"status\" style=\"max-width:256px\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var36 string
|
||||
templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("loading"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 147, Col: 66}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 149, Col: 66}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "</p><script async type=\"module\" src=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "</p><script async type=\"module\" src=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var37 string
|
||||
templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.JoinStringErrs(anubis.BasePrefix + "/.within.website/x/cmd/anubis/static/js/bench.mjs?cacheBuster=" + anubis.Version)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 148, Col: 138}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 150, Col: 138}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var37))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "\"></script><div id=\"sparkline\"></div><noscript><p>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "\"></script><div id=\"sparkline\"></div><noscript><p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var38 string
|
||||
templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("benchmark_requires_js"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 151, Col: 45}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 153, Col: 45}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var38))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "</p></noscript></div></div><form id=\"controls\" style=\"position:fixed;top:0.5rem;right:0.5rem\"><div style=\"display:flex;justify-content:end\"><label for=\"difficulty-input\" style=\"margin-right:0.5rem\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "</p></noscript></div></div><form id=\"controls\" style=\"position:fixed;top:0.5rem;right:0.5rem\"><div style=\"display:flex;justify-content:end\"><label for=\"difficulty-input\" style=\"margin-right:0.5rem\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var39 string
|
||||
templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("difficulty"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 157, Col: 88}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 159, Col: 88}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "</label> <input id=\"difficulty-input\" type=\"number\" name=\"difficulty\" style=\"width:3rem\"></div><div style=\"margin-top:0.25rem;display:flex;justify-content:end\"><label for=\"algorithm-select\" style=\"margin-right:0.5rem\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "</label> <input id=\"difficulty-input\" type=\"number\" name=\"difficulty\" style=\"width:3rem\"></div><div style=\"margin-top:0.25rem;display:flex;justify-content:end\"><label for=\"algorithm-select\" style=\"margin-right:0.5rem\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var40 string
|
||||
templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("algorithm"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 161, Col: 87}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 163, Col: 87}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "</label> <select id=\"algorithm-select\" name=\"algorithm\"></select></div><div style=\"margin-top:0.25rem;display:flex;justify-content:end\"><label for=\"compare-select\" style=\"margin-right:0.5rem\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "</label> <select id=\"algorithm-select\" name=\"algorithm\"></select></div><div style=\"margin-top:0.25rem;display:flex;justify-content:end\"><label for=\"compare-select\" style=\"margin-right:0.5rem\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var41 string
|
||||
templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("compare"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 165, Col: 83}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 167, Col: 83}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "</label> <select id=\"compare-select\" name=\"compare\"><option value=\"NONE\">-</option></select></div></form>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "</label> <select id=\"compare-select\" name=\"compare\"><option value=\"NONE\">-</option></select></div></form>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user