feat: first implementation of honeypot logic (#1342)
* feat: first implementation of honeypot logic This is a bit of an experiment, stick with me. The core idea here is that badly written crawlers are that: badly written. They look for anything that contains `<a href="whatever" />` tags and will blindly use those values to recurse. This takes advantage of that by hiding a link in a `<script>` tag like this: ```html <script type="ignore"><a href="/bots-only">Don't click</a></script> ``` Browsers will ignore it because they have no handler for the "ignore" script type. This current draft is very unoptimized (it takes like 7 seconds to generate a page on my tower), however switching spintax libraries will make this much faster. The hope is to make this pluggable with WebAssembly such that we force administrators to choose a storage method. First we crawl before we walk. The AI involvement in this commit is limited to the spintax in affirmations.txt, spintext.txt, and titles.txt. This generates a bunch of "pseudoprofound bullshit" like the following: > This Restoration to Balance & Alignment > > There's a moment when creators are being called to realize that the work > can't be reduced to results, but about energy. We don't innovate products > by pushing harder, we do it by holding the vision. Because momentum can't > be forced, it unfolds over time when culture are moving in the same > direction. We're being invited into a paradigm shift in how we think > about innovation. [...] This is intended to "look" like normal article text. As this is a first draft, this sucks and will be improved upon. Assisted-by: GLM 4.6, ChatGPT, GPT-OSS 120b Signed-off-by: Xe Iaso <me@xeiaso.net> * fix(honeypot/naive): optimize hilariously Signed-off-by: Xe Iaso <me@xeiaso.net> * feat(honeypot/naive): attempt to automatically filter out based on crawling Signed-off-by: Xe Iaso <me@xeiaso.net> * fix(lib): use mazeGen instead of bsGen Signed-off-by: Xe Iaso <me@xeiaso.net> * docs: add honeypot docs Signed-off-by: Xe Iaso <me@xeiaso.net> * chore(test): go mod tidy Signed-off-by: Xe Iaso <me@xeiaso.net> * chore: fix spelling metadata Signed-off-by: Xe Iaso <me@xeiaso.net> * chore: spelling Signed-off-by: Xe Iaso <me@xeiaso.net> --------- Signed-off-by: Xe Iaso <me@xeiaso.net>
This commit is contained in:
parent
cb91145352
commit
122e4bc072
25 changed files with 968 additions and 84 deletions
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue